Skip to content

feat(cars): the M2O export streams each car with its own material library - #12

Merged
Segfaultd merged 3 commits into
mainfrom
feat/m2o-car-materials
Oct 9, 2026
Merged

Segfaultd merged 3 commits into
mainfrom
feat/m2o-car-materials

Conversation

@Segfaultd

@Segfaultd Segfaultd commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

car_export_m2o now writes exactly what Mafia II Online streams: the car's archive untouched, plus a material library when the car adds materials. Server owners drop the folder in and ship nothing else.

Pairs with mafia2online/Mod#16, which adds streamed cars to M2O (that PR depends on MafiaHub/Framework#314).

What the export writes

<resource>/
  package.json                      name, version, description: no file list needed
  stream/sds/cars/<name>.sds        the car, byte for byte as built
  stream/sds/cars/<name>_z.sds      winter twin, when there is one
  stream/materials/<name>.mtl       only when the car adds materials
  • Gone: vehicles.json, the sds/** file list, the table rows and the hashes. M2O registers each archive by its file name (a stock car's name replaces that car), so none of it is read any more.
  • Old layout: an export folder in the old layout (sds/cars + vehicles.json) is refused with a clear message instead of being mixed into.

Materials travel as a streamed library

The meshes name materials by hash, and the game resolves them against the libraries in edit\materials, which no server can change on a player's machine.

  • Native path: the game already loads a library in addition to default.mtl: a mission pack's, through C_MaterialManager::LoadMTL, released again when the mission closes. M2O streams libraries the same way, for any content (cars now, city parts and crash objects later).
  • What's written: each material the car uses that the game didn't ship with (one the toolkit created for it) goes into an MTL v57 library of just those materials, written atomically. A re-export that adds nothing removes a stale library.
  • What "shipped with" means: each library in edit\materials as it was before the toolkit first wrote it. That's its oldest backup in backups\, or the file itself if the toolkit never wrote it.
  • Refused:
    • a car whose meshes use a material no library has (empty hash-0 slots are ignored);
    • a car named with anything but a-z, 0-9 and _, which a server would not stream;
    • a car that adds materials under a default* name, whose library would collide with the game's own.

The M2O server refuses a car that adds textures no streamed material uses, so a car exported without its library won't ship.

A clone carries its own vehicles.tbl row

Without a row, a new car takes the game's empty first row in M2O. The export now writes a clone's row into both archives as a native table patch, /tables/patch_m2o<name>_vehicles.tbl, which the game appends while the car is loaded (how Joe's Adventures adds its cars).

  • Id and title: the row keeps the id and title text of the car it was made from. The game indexes per-car data by id, and a non-stock id crashes; the clone's text exists only on its maker's machine.
  • No paint row: the car uses its source car's paint row. The clone copies that row anyway, and a second row under a shared id would also repaint the stock car.
  • Stock names: a car under a stock name keeps the stock row.
  • New helpers: GameTable.PatchRow and GameTable.Name, plus SdsManifest.AddTableEntry.
  • The archives are no longer byte-for-byte copies for clones: they are the built archive plus that one Table entry.

Testing

  • --probe-car-m2o against a real install (Steam, Mafia II Classic): 28/28 pass. It covers:
    • each archive being the built one plus its row (the clone's name, Shubert_38's id 14 and title);
    • the new layout and package.json;
    • a stock-material clone shipped with no library;
    • one of the car's materials treated as new: stream/materials/<car>.mtl holds exactly it, the archives stay byte for byte, and a re-export without it removes the library;
    • the refusals: bad resource name, foreign folder, old layout, unreadable package.json, misnamed archive, a car name a server does not stream, unknown material.
  • End to end with M2O, using the tester's FBI Shubert (shubert_38_custom.sds, whose FBI_Empire_Bay and FBI_beacon aren't in the stock libraries). ExportFrom wrote the untouched archive plus a 251-byte shubert_38_custom.mtl. Used unchanged as an M2O server resource:
    • the server accepted it, and refused it once the library was removed;
    • the client loaded the library (2 materials, both new), and the car showed the FBI door emblem;
    • the export with the row gave the car Shubert_38_Custom's row (id 14, 14 paint combos) instead of the empty row 0;
    • the two-client streamed suite passed 9/9 (seats, driving, stream out and in, virtual world, destroy);
    • leaving the server released the materials, and reconnecting in the same game process loaded them again.
  • The solution builds with 0 warnings and 0 errors.

Not covered

  • A new car shows its source car's title and paint, for the reasons above.
  • --probe-car-writes was already failing before this PR: it lists 14 files outside its allow-list. CarM2oExport was one of them before this change, and nothing here adds to that list.

…ials inside

Mafia II Online now registers every archive a resource ships under
stream/sds/cars/ by its file name and checks it before any player
downloads it, so the export writes just package.json and the archives
there: no vehicles.json, no file list, no table rows.

A material the car uses that the game did not ship with travels inside
its archive: the export writes those materials as an MTL library and
embeds it as a MemFile named <name>.mtl, which the multiplayer adds
through the game's LoadMTL. "Shipped with" is each library in
edit\materials as it was before the toolkit first wrote it (its oldest
backup). A car using a material no library has is refused, an export
folder in the old layout is refused instead of mixed in, and empty
material slots (hash 0) are ignored.

--probe-car-m2o covers the layout, a car with only stock materials
shipped byte for byte, an added material embedded in the archive that
uses it and in no other, and the refusals.
The M2O export no longer embeds the materials a car adds as a MemFile
inside its archive. They go into a library of their own,
stream/materials/<name>.mtl, which Mafia II Online loads in addition to
the game's libraries the way the game loads a mission pack's (LoadMTL),
and releases when the player leaves the server. The same path serves
any streamed content, not only cars, and the archives now always ship
byte for byte as they were built.

- The library is written through AtomicFile; MaterialLibrary gains
  ToBytes() for it. A re-export that adds nothing removes a stale one.
- A car named with anything but a-z, 0-9 and _ is refused, as a server
  would; so is one that adds materials under a default* name, which
  would collide with the game's own libraries.
- The result's EmbeddedMaterials becomes Materials (MCP model too).
- --probe-car-m2o covers the library and the name refusal: 28/28.
@Segfaultd Segfaultd changed the title feat(cars): the M2O export streams one archive per car, its new materials inside feat(cars): the M2O export streams each car with its own material library Oct 9, 2026
A car exported under a new name had no row in the multiplayer and took
vehicles.tbl's empty first row. Its archives now carry the row as a
native table patch, /tables/patch_m2o<name>_vehicles.tbl, which the game
appends while the car is loaded (how Joe's Adventures adds its cars).
The row keeps the id and title text of the car it was made from: the
game indexes per-car data by id, and the clone's text is local to its
maker. A car under a stock name keeps the stock row.

- GameTable.PatchRow builds a one-row patch of a table; GameTable.Name.
- SdsManifest.AddTableEntry writes a Table entry
  (Type, NumTables, Table..., Version).
- --probe-car-m2o checks each archive is the built one plus that row:
  28/28.
@Segfaultd
Segfaultd merged commit b27d962 into main Oct 9, 2026
1 check passed
@Segfaultd
Segfaultd deleted the feat/m2o-car-materials branch October 9, 2026 10:22
raighen added a commit to raighen/illusion-toolkit that referenced this pull request Oct 9, 2026
…ts own material library (mafia2online#12)

car_export_m2o now writes the layout Mafia II Online streams:
package.json, stream/sds/cars/<name>.sds with its winter twin,
stream/materials/<name>.mtl for the materials the car adds, and the
car's vehicles.tbl row inside its archives.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant