Skip to content

fix(snapshot): restore refuses moved static geometry and voxel sensors - #425

Merged
siracozmen01 merged 1 commit into
mainfrom
fix/restore-static-geometry
Nov 8, 2026
Merged

siracozmen01 merged 1 commit into
mainfrom
fix/restore-static-geometry

Conversation

@siracozmen01

Copy link
Copy Markdown
Collaborator

Problem

Planes, meshes, height fields and voxel chunks are static geometry. A create puts them only on a static body, through ShapeContentCheck in src/shape.c, and m3Body_SetType refuses to make such a body kinematic or dynamic, through m3TypeChangeCheck in src/body.c. A voxel chunk is also never a sensor: create refuses a sensor def, and no setter changes a shape's sensor flag.

m3World_Restore checked neither rule. A probe on main took one shape of each kind on a static body, changed one byte of its snapshot and restored it into the same world. Each kind restored and stepped with m3_success on a body made kinematic, and a voxel chunk restored and stepped as a sensor (#424).

Change

  • StaticGeometryValid in src/snapshot_values.c runs with the other value checks, after the shape lists have put every live shape's body in range and alive. It refuses, with m3_errorFormat, a live plane, mesh, height field or voxel chunk whose body is not static, and a live voxel chunk that is a sensor. A refused restore leaves the world as it was.
  • m3ShapeIsStaticOnly in src/shape.h names the four types once. Create, the type change and restore all use it, so the three checks cannot drift apart.

No step, result or golden hash moves: the check only refuses snapshots the library cannot write. Mesh, height field and plane sensors stay legal, as create allows them.

Verification

  • test/test_restore_links.c has two new forged cases on the fuzz scene: its mesh's body set to kinematic, and its voxel chunk set to a sensor. Each is refused with the world left as it was. The suite's existing cases still restore every snapshot the engine writes.
  • With StaticGeometryValid made to pass everything, which is main's restore, both cases restore and the test fails. With it, a gcc-14 Debug build with -DMAUL3D_WERROR=ON passes the test.
  • tools/check.sh pr passes on this head, a9277bb: 78 and 76 tests, clang-tidy clean, and fuzz with no finding.

Closes #424

@siracozmen01 siracozmen01 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at a9277bb: ready, once CI goes green. I read the code and did not run it.

  • The helper moves nothing. m3ShapeIsStaticOnly is the same four-type disjunction both sites had. ShapeContentCheck still refuses a static-only type on a body that is not static. m3TypeChangeCheck still runs its loop only for type != m3_staticBody and returns m3_errorInvalid on the first static-only shape. So for every shape type and every body type, create and SetType give the answers they gave before.
  • Restore refuses what create and SetType cannot produce, and only that.
    • Create puts a static-only shape only on a static body, and SetType refuses to move such a body off static. No live static-only shape can sit on a kinematic or dynamic body, and that is exactly what StaticGeometryValid refuses.
    • A static body holding such shapes is skipped. So is every other shape type, sensors of planes, meshes and height fields included, which create allows.
    • It runs inside m3SnapshotValuesValid, after ShapeListsValid (snapshot_links.c:571-573). The body index it reads has already passed that check, as its comment says.
  • The voxel-sensor rule matches create. Create refuses a voxel chunk with def->isSensor both in shape_create.c:664 and in ShapeContentCheck (!def->isSensor && ..., shape.c:207). It stores shapeSensor as def->isSensor ? 1 : 0, and no setter writes it. So a live voxel chunk always has shapeSensor == 0, and sensors[s] != 0 refuses exactly the forged ones.
  • The forged cases. Each changes one byte the library never writes that way: the mesh's body type to kinematic, the voxel chunk's sensor byte to 1. Each expects m3_errorFormat with the world unchanged, through the suite's case table. The body reports that both restore with the check made to pass everything.
  • fix without ! fits at 1.x: restore now refuses bytes the library never writes. The body has Problem, Change and Verification, and no column-0 name( line.

Whichever of this and #423 merges second takes a merge of main into test_restore_links.c, as the manager noted. That resolution needs a read before it merges.

No findings.

@siracozmen01
siracozmen01 merged commit 4a5b992 into main Nov 8, 2026
28 checks passed
@siracozmen01
siracozmen01 deleted the fix/restore-static-geometry branch November 8, 2026 13:48
@rawframe-release rawframe-release Bot mentioned this pull request Nov 8, 2026
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.

Restore takes static geometry on a kinematic body and a voxel sensor

1 participant