Repository navigation
Custom Edition maps downloaded before joining (archives + BitTorrent), keeping network version 27 - #315
Closed
FractumSeraph wants to merge 3 commits into
Closed
Custom Edition maps downloaded before joining (archives + BitTorrent), keeping network version 27#315FractumSeraph wants to merge 3 commits into
FractumSeraph wants to merge 3 commits into
Conversation
Reverts a2aad18 ("Custom Edition map sharing") and 71ceb36 ("Players who already have the map no longer wait for a machine that is still downloading it."), which this pull request replaces: a Custom Edition map a client lacks is downloaded before it joins, from the community's map archives or over BitTorrent (the commits that follow), and no map a player has is ever renamed. Gone with it: the map sharing transfers, the players' sessions with each other for it, the renaming of maps for their hashes, the MAP SHARING settings screens and the network.map_* settings. HALO_PORT_NETWORK_VERSION stays 27, and builds 189 and 190 (version 27, with map sharing) play with these as with a version 27 machine that does not share maps (port/linux/NETCODE.md, version 27): - a request for the game's map (distributed message 83) is answered with version 27's own "off" offer (port/linux/game/map_share_decline.c; once in five seconds a machine, at most), so that player is told at once that the host does not share its maps; the other map sharing messages (83 to 86) are dropped, as any message of an unknown type already is, and the numbers stay reserved; - the maps those builds renamed in a player's custom_maps (<name>_<hash>.map) stay as they are and play, and a version 27 host's hash-named levels are found by their names: the map downloads' commit (custom_edition_cache.c, custom_edition_maps.c, map_torrents.c). Internet play's signalling is byte for byte the same (27 only reused the join and accept messages to let players reach each other for maps). Checked, on the loopback in Docker against OpenCE's build-190 release and ChupathingyCE 0.7.3e's dedicated server (both version 27): build-190's host and client play with these builds' on a stock map both ways; these builds' client joins the 0.7.3e server; build-190's client without the map asks this build's host for a hash-named map and is declined at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The loading screen (interface/progress_bar.c: a light sweeping from left to right over a picture, with a glowing trail) drew nothing: it uses the fixed-function pipeline (SetVertexShader with 0), which d3d8_gl.c did not have, and its picture, loading.tga, is on no retail disc. - d3d8_gl.c draws fixed-function immediate-mode draws with a vertex shader of its own (nv2a_fixed_function_glsl), positions transformed by the world, view and projection matrices; and keeps a front buffer (a 640x480 render target taking the middle of the frame at BlockUntilVerticalBlank) for GetBackBuffer with -1, the frame before, whose blur makes the light's trail. The screen is drawn in the middle 640 columns, as the menus are on a wide screen. - After it, the rasterizer's caches of the device's states and of its vertex shader are made the device's again (rasterizer_forget_hardware_state, rasterizer_vertex_shader_runtime_forget): it sets them itself; and SelectVertexShader (a packed program) always leaves the fixed-function pipeline. - On OpenGL ES (Android, the web) the fixed-function shader turns Direct3D's clip space as the others do (HALO_GLES); without OpenGL (debug.null_renderer) it is not compiled. - The picture is the Xbox's own: the retail default.xbe holds it (320 by 240, runs of 4-bit greys after its width and height), and importing a disc image now writes it into the maps folder as loading.tga: port/linux/src/ xiso.c, the Android app's XisoExtractor.java and the web page's xiso-worker.js. Without one (a maps folder copied by hand) the light sweeps over clouds the game makes. No game data is in the repository. - progress_bar.c also has the mode the next commit's download screen uses (a share given, from 0, drawn over the menus, and let go of). - A picture that cannot be written whole is not left cut short (the loading screen would read past it). Checked: port/web/tests/xiso.test.js takes the picture out of made-up executables (and an image's default.xbe); the Java decoder gives the same bytes as the JavaScript one; xiso.c's picture from a Rev 2 disc image matched a separate decoder's byte for byte; the Linux and web builds in Docker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ver BitTorrent A machine joining a game on a Custom Edition map it lacks (or has another version of, or without Custom Edition's resource maps) downloads it before the join, and joins by itself once it is there; the host seeds the map it plays. Design and details in docs/map_torrents.md. - The index of the maps' torrents (port/assets/network/map_torrents.txt, tools/map_torrents.py): 5,829 maps and the three resource maps, by file name and header checksum (the map version the host already sends), and for 5,826 of them the community archives' zips that hold them. - The archives first (port/linux/src/map_archives.c, maps.archives): HaloNet's zip by its URL, HaloMaps's as its Download button fetches it, over HTTPS with the self-updater's download (update_request in update.h: a POST, a user agent and a stop flag added to posix_update.c and win32_update.c), the map taken out of the zip (map_archives_zip.c, every offset checked, its CRC too). What they give is added to the BitTorrent client as a seed, which hashes it against the info hash; what they do not, or what is not the index's, comes over BitTorrent. - The BitTorrent client (port/linux/src/torrent*.c, on the platform layer's sockets and threads: BEP 3, 9, 10, 11, 15, 19), its downloads and seeding (map_torrents.c), the [maps] settings, and the hooks: ui_widget_port_join waits for the map (menu_functions.c, which posts the join again), cache_files_map_present downloads a map the host changed to, the server's listing and the client's settings seed the map played. - No map a player has is replaced, renamed or changed: another version of one goes in custom_maps\versions\<checksum>\<name>.map, which a client of a game on that version plays (custom_edition_cache.c, custom_edition_map_version_forget once it leaves); the map lists show only custom_maps itself. - The download screen (port/linux/game/download_screen.c, maps.download_screen): Halo's loading screen over the menus at the download's share, the map's name over it, B to stop it; the menus go on behind it (nothing drawn without a renderer, debug.null_renderer). - The downloads and seeding are polled each frame from main.c's loop, a window or not (a server, debug.null_renderer). - The builds put map_torrents.txt beside the game (and in the APK); the web build leaves the client out (port/web/src/web_map_torrents.c). - tools/torrent_check.c runs the client and the unzip alone; ninja linux builds it. - Network version 27's hosts and players (builds 189 and 190): a host's level name with the map's hash after it (custom_maps\<name>_<hash>) is found as <name>.map (custom_edition_cache.c, map_torrents.c), and the maps those builds renamed for their hash in a player's custom_maps stay as they are, found for a host's custom_maps\<name> when their checksum is the host's and listed without the hash (custom_edition_maps.c). Nothing goes over the game's connections: no network version of its own. The client is careful with what peers send: a metadata piece only as asked of the peer, of the size it told (one stalled at another size let go of); a block only as asked; a peer whose blocks fail a piece's hash banned (at once when it sent the piece alone, else at its third) and not connected to again; requests served of two blocks at most, their offsets checked without overflow; four incoming connections from one address at a time. Names are looked up on a thread of their own and the game's thread reads a torrent's status without waiting for the client's (whose lock a lookup or a file's hashing holds); one HTTPS request at a time over Mbed TLS (no threading support in it); a file moved into place never replaces one there (on POSIX, whose rename would, the name is taken first with O_EXCL); a download's file is not opened through a link; the lists of files to fetch are tab-separated (some maps' names have commas); an index entry of checksum 0 is that version, not any; a map name with a pattern's characters (* ?) is never listed for; a web seed's server is looked up before a piece is taken for it; files are hashed 4 MB a tick under the client's lock. Checked: the Linux CI steps in Docker (301 passed, test_profile's ThreadSanitizer check failing only as Docker forbids it, with the new tools/test_torrent_check.py: a file seeded and downloaded on the loopback, a peer with other data refused, maps taken out of made-up zips and damaged ones refused; tools/test_map_torrents.py with the sources); the web build and its tests; the Windows files' compile (syntax, MSVC's and the SDK's headers). End to end on Linux in Docker, two machines on the loopback: a client with another version of chaosgulch got the host's over BitTorrent into custom_maps\versions\7B309554 (its own copy untouched) and played it on the next join; with no trackers or web seeds, a client without chaosgulch got it out of HaloMaps's zip, and one with another version of battlecity_lvl1 got the host's out of HaloNet's into the versions folder and played it; each the same as the host's, byte for byte. Against build-190 (network version 27): its host's hash-named chaosgulch was downloaded by its name and played; a client holding only a map build-190 renamed played it for a host's plain chaosgulch (no download, the file untouched); this build hosting a renamed map, build-190's client was declined, this build's client downloaded it by its name and played, and build-190's client holding it played. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FractumSeraph
force-pushed
the
map-torrents-pr
branch
from
October 13, 2026 11:44
6466596 to
f743723
Compare
FractumSeraph
force-pushed
the
map-torrents-pr
branch
from
October 13, 2026 12:06
f743723 to
6466596
Compare
Author
|
Closing in favour of #335, which downloads the maps from the archives and a web seed on top of main as it is (network version 28, map sharing kept), without a BitTorrent client. It builds on #333 (maps keep their names) and #334 (the loading screen). Thanks, and thanks Pablo for flagging the web build merge problem. |
FractumSeraph
pushed a commit
to FractumSeraph/OpenCE
that referenced
this pull request
Oct 16, 2026
…-refresh README: badges and a Builds section
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Custom Edition maps a player lacks are now downloaded before the join, over BitTorrent, from the map archive's seeds, its web seed and other players. This replaces the host-to-player map sharing of builds 189 and 190. The Xbox loading screen now draws, and covers the menus while a map downloads.
What it does
port/assets/network/map_torrents.txt, 5,829 maps plusbitmaps.map,sounds.mapandloc.map). The lookup uses the file name and the header checksum the host already sends as the map version. The map downloads, and the join goes ahead by itself once it is there. Resource maps that are missing come the same way. If the host changes map after the join, the map downloads and the player is told to join again..mapfile is ever replaced, renamed or changed. When the host's map is another version of one the player has, the host's version goes beside it, under its own name, incustom_maps/versions/<checksum>/<name>.map. A client in that game plays that copy, and the player's own copy stays as it was. The map lists show onlycustom_mapsitself, so hosting still uses the player's own copy.maps.upload_limit(512 KB/s), so the game's traffic comes first. Withmaps.seed = "all", clients seed too.interface/progress_bar.c) drew nothing before: it uses the fixed-function pipeline, whichd3d8_gl.cdid not have. It now draws, including the light's trailing glow, which reads the previous frame throughGetBackBuffer(-1).default.xbeholds it. Importing a disc image now writes it tomaps/loading.tga: on desktop (xiso.c), Android (XisoExtractor.java) and the web page (xiso-worker.js). Without a disc image's picture, the light sweeps over clouds the game generates. No game data is in the repository.port/linux/src/torrent*.c, about 4,000 lines on the platform layer's sockets and threads). It speaks BEP 3, 9, 10, 11, 15 and 19 (peers, metadata, PEX, UDP and HTTP trackers, HTTP web seeds). It does not speak the DHT, uTP, encryption or HTTPS. The web build leaves it out: a page has no TCP or UDP sockets and plays no Custom Edition maps.Design, settings and the archive's servers are described in
docs/map_torrents.md. The[maps]settings are inport/linux/README.md.Network version
This PR does not add a network version. It removes the map sharing commits (a2aad18, 71ceb36), which were the only change in version 27. The torrent downloads send nothing over the game's connections. So the protocol is version 26's again, and
HALO_PORT_NETWORK_VERSIONgoes back to 26:NETCODE.mdsays so.Servers
The defaults point at the archive I run:
halovps.fractumseraph.net:6969, UDP and HTTP. It takes only the index's torrents.http://halomaps.fractumseraph.net/maps/.The settings
maps.trackers,maps.web_seedsandmaps.torrent_indexcan point anywhere, and anyone can seed the.torrentfiles.Checked
tools/ci_build.py linux release):tools/test_map_torrents.pyand the newtools/test_torrent_check.pyadded;tools/test_light_storage.py.tools/ci_build.py web release;node --test "port/web/tests/*.test.js";tools/test_web_build.py.tools/test_torrent_check.py:ninja linuxnow also buildsbuild/linux/torrent_check. One copy seeds a made-up file on the loopback and another downloads it, which must give the file byte for byte. A peer with the wrong data gives nothing.port/web/tests/xiso.test.js: the loading picture taken out of made-up executables.chaosgulch, and a client whosecustom_maps/chaosgulch.mapis another version (header checksum changed).custom_maps/versions/7B309554/(same bytes as the host's); the client's own copy was untouched.map_torrents.txtas they copybrokers.txt. The client and the hooks are the same code the Linux build compiles.🤖 Generated with Claude Code