Repository navigation
build(deps)!: move Maul2D to 1.3.1 - #225
Merged
Merged
Conversation
Vendor Maul2D 1.3.1 from its release archive and follow its renames, reordered list queries and new statuses. The 2D step refuses a tick Maul2D cannot step before it changes anything, and the statistics count only kinematic targets carried out. BREAKING CHANGE: the engine is built on Maul2D 1.3.1. The 2D physics digest changes for every world, since Maul2D's world hash now covers each shape's broadphase box and each body's sleep threshold; body poses are unchanged. Queries between steps see a shape that moved more than 0.1 m in the last step where it is now. A body with a locked rotation no longer turns when given a spin. The 2D step fails at a tick shorter than 1 microsecond or longer than 10 seconds, and the statistics count only kinematic targets carried out.
siracozmen01
commented
Nov 5, 2026
siracozmen01
left a comment
Contributor
Author
There was a problem hiding this comment.
Review at 2342a0b: no change needed. I read the engine's changes and the Maul2D parts they depend on; I did not run anything.
- The step check is exactly Maul2D's own.
M2_MIN_STEPis1.0e-6fandM2_STEP_LIMITis10.0f(third_party/maul2d/include/maul2d/base.h:266-267). Maul2D'sm2CheckTimeStep(src/core.h:267, throughm2CheckPositiveandm2CheckRange) acceptsx > 0 && x >= lo && x <= hi. The engine compares the same floatkSecondsagainst the same float constants with>=and<=, so the two agree at both ends and on NaN.- The other refusals in
WorldStepWorkerare: substeps (already held to 1 to 64, andM2_MAX_SUBSTEPSis 64, base.h:206), andm2CheckWriter(src/world_internal.c:131), which refuses only a null world or a call from inside Maul2D's host code. The step system is neither, so theRAWFRAME_CHECKcannot fire on a checked tick. - Moving
kSecondsabove the body sweep makes the refusal come before anything changes, and the test'sbodiesMade == 0pins that.
- The other refusals in
- Kinematic targets.
m2Body_SetTargetTransformnow returns a status, and the engine counts onlym2_success. The new test case moves the paddle by about 3 km in one tick, which is over 100 km/s at the scene's rate. It is refused and cleared, and the unchangedtargets == 31would be 32 on the old counting, so the assertion can fail. The paddle staying at x = 3 pins that the body did not move. - The calls 0.7.0 to 0.9.0 reorder and rename are all updated:
m2World_OverlapCircle(both calls),m2World_CollideMover,M2_MAX_MOVER_PLANES,maxMotorEfforton the revolute and prismatic defs,enableBullet, andm2IsCpuBackendSupportedread asbool. The 0.10.0 null-array refusal does not reachoverlapCircle, since its vector is resized to at least 16 first. - CMakeLists.txt. In the pull request's file list,
src/snapshot_pairs.cis the only new.cunderthird_party/maul2d/src/. The two other new files,snapshot_pairs.handsnapshot_read.h, are headers, and no source is removed, so the one added line is the whole change to the source list. I did not compare against the archive itself; that rests on your byte comparison. - Body. It has no column-0
word(ortype(scope):line, and itsBREAKING CHANGE:footer covers the four behavior changes a game can see.
Merged
siracozmen01
pushed a commit
that referenced
this pull request
Nov 5, 2026
🤖 I have created a release *beep* *boop* --- ## [0.8.0](v0.7.0...v0.8.0) (2026-11-05) This release moves the engine to Maul2D 1.3.1. Body poses step to the same bits as before, but the 2D physics digest changes for every world, and a few edge cases of 2D physics act differently. No wire, save, checkpoint or cooked format changes. ### Changed - **Breaking:** The engine is built on Maul2D 1.3.1, from 0.6.0, vendored from its release archive with its SHA-256 checked. What a game sees change ([#225](#225)): - The 2D physics digest changes for every world, since Maul2D's world hash now covers each shape's broadphase box and each body's sleep threshold. Body poses are unchanged: a scene of 20 bouncing balls ends on the same bits after 120 ticks. - Rays, sweeps, overlaps and character moves between steps see a shape that moved more than 0.1 m in the last step where it is now, instead of missing it or finding it where it was. - A body with a fixed rotation no longer turns when a game gives it an angular velocity or an angular impulse. - A bullet body passes no wall when more than 64 shapes crowd its path, and a character meeting more than 16 surfaces keeps those of the lowest shape slots. - The 2D step fails at a tick shorter than 1 microsecond or longer than 10 seconds, before it changes anything, instead of leaving the world standing still. A dedicated server with 2D physics takes a `world.tick_rate` of 1 to 1,000,000. - `Physics2DStatistics::targets` counts only kinematic targets carried out. A target too far to reach in one tick within Maul2D's 100 km/s speed limit is cleared, as before, but no longer counted.
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.
Problem
The engine pins Maul2D 0.6.0. Maul2D 1.3.1, the latest release (tag
v1.3.1, cf08f1f), is released and verified. Between them lie nine 0.x releases (0.7.0 to 0.11.4), five of them breaking, then 1.0.0, which settles the API, and the 1.1, 1.2 and 1.3 minor releases with their patches.Change
The engine moves to Maul2D 1.3.1, vendored from its release archive checked against the release's
SHA256SUMSwithtools/update_maul.sh.third_party/maul2d/: upstream'sinclude/,src/andLICENSEat 1.3.1. The engine's ownCMakeLists.txtgainssrc/snapshot_pairs.c, the one source file upstream added; its flags already match upstream's (-ffp-contract=off, no fast math, C23, AVX2 and FMA kernels on x64).modules/physics2d/: the calls the renames and reorders below touch, and two status checks the library now returns.docs/operations/dependencies.md: the pin row.docs/internals/physics-and-navigation.md: the tick lengths the 2D step takes, and the kinematic target it does not carry out.docs/user/dedicated-server.md: theworld.tick_ratea game with 2D physics takes.What moves
modules/physics2d/tests/physics_test.cpp: the 2D physics digest golden moves from0xc42bbb4bf908bd7fto0x57dd106d48ba3973. The scene's 20 ball poses after 120 ticks are the same bits on 0.6.0 and 1.3.1 (an FNV-1a hash of all of them is17cf21929a83818fon both), so only what Maul2D's world hash covers moved: the broadphase boxes (0.11.4) and the sleep thresholds (1.3.0).Physics2DStatistics::targetscounts only targets carried out (0.9.0 below).Each release and what it does to the engine
Every engine call into Maul2D is in
modules/physics2d/src/(physics.cpp,characters.cpp,joints.cpp). The engine never writes a Maul2D snapshot or journal tape and keeps none on disk, so no format change below reaches a file of ours.0.7.0
m2World_OverlapCircletwice inPhysics2D::overlapCircleandm2World_CollideMoverincharacters.cpp, all reordered. Nothing they return changes.M2_MOVER_PLANESisM2_MAX_MOVER_PLANES(16) incharacters.cpp;maxMotorTorque(revolute) andmaxMotorForce(prismatic) aremaxMotorEffortinjoints.cpp. Same meaning, same values.0.8.0
m2CpuSupportsBackendism2IsCpuBackendSupported(it returnsboolfrom 0.9.0), andm2BodyDef.isBulletisenableBullet, both inphysics.cpp. The engine calls none of the other renamed functions.0.9.0
m2World_Stepreturnsm2Resultand must be read. The engine checks the tick's length against Maul2D'sM2_MIN_STEPtoM2_STEP_LIMIT(1 microsecond to 10 seconds) at the start of the step system, before it changes anything, and fails the system withPhysics2DError::InvalidSettingsoutside it. On 0.6.0 such a tick was refused inside Maul2D without a word and the world stood still. Substeps were already held to 1 to 64, which isM2_MAX_SUBSTEPS, so a refused step after that check is an engine fault and stops the process (RAWFRAME_CHECK). Every configured rate the engine ships (60 Hz and the tests' rates) is inside the range.m2Body_SetTargetTransformreturnsm2Result. A target too far to reach withinM2_SPEED_LIMIT(100 km/s) in one tick was refused on 0.6.0 too, without a status, andPhysics2DStatistics::targetscounted it anyway. The engine now counts only targets carried out, as that field's comment says. The body still stays where it was, and the target is still cleared.m2BodyTypeas anint32_ttypedef, the water volume rename, the removed math helpers. The engine reads no counter or profile, uses no water volume or particle, and casts tom2BodyType, which still compiles.hitOuton a miss. The engine reads a hit only when the call returns true, and a list only up to the count returned, so neither reaches it.0.10.0
m2ComputeHullkeeps fine curves. The engine's polygons are boxes and tile rectangles of four corners each, whose hulls are the same.0.11.0
0.11.1
0.11.2 and 0.11.3
m2World_CollideMoverwith a short array. The engine has no particles. A bullet body (Body2D::bullet) with more than 64 shapes along one substep's sweep now stops at a wall it could pass before. A character whose capsule meets more than 16 planes keeps those of the lowest shape slots instead of the first the trees yield; with 16 or fewer it gets the same planes in the same order.0.11.4
m2World_Hashcovers the boxes and moved flags, soPhysics2D::digestmoves for any world with a moving body. The engine's queries run between steps: Kest's rays, sweeps and overlaps,castRayAt, and each character's moves, which collide its capsule against the world before the step. A shape that moved more than 0.1 m in the last step is now found where it is, instead of missed or found where it was.1.0.0 and 1.0.1
m2DecomposeOutline, which the engine does not call.1.1.0
m2DecomposeOutlinereturns new pieces. The engine does not call it.1.2.0
1.2.1
1.2.2
Body2DwithfixedRotationgiven an angular velocity (byVelocity2Dor an angular impulse) no longer turns. On 0.6.0 it turned at that speed with nothing to slow it.1.3.0
Physics2D::digestmoves for every world. The engine keeps the default threshold, so no body sleeps differently.M2_NODISCARDworks under GCC 14's C23. It reaches the two calls above, which the engine now reads.1.3.1
boolresults checked inside Maul2D. Nothing the engine sees changes.Verification
physics2d's tests: with the old golden,TheSameWorldAndWritesGiveTheSameBitsfailed only at the digest. A probe hashed the scene's 20 ball poses after 120 ticks on both builds:17cf21929a83818fon 0.6.0 and on 1.3.1. The new digest holds in every tree below, so the bytes are the same on Clang, GCC 14 and wasm32.tools/check.sh fullpassed at four jobs through the heavy-job wrapper, from cold trees, in 3850 s:clang-thread: 208 of 208.clang-sanitize: 253 of 253.gcc-debug(GCC 14): 208 of 208.clang-development: 208 of 208 unit tests and 19 of 19 integration tests, the played games among them.wasm-development: 62 of 62.gcc-debugtree with GCC 14 and Maul2D's own-Wall -Wextra: 0 warnings, so 1.3.0's workingM2_NODISCARDfinds no dropped status in Maul2D.BREAKING CHANGE: the engine is built on Maul2D 1.3.1. The 2D physics digest changes for every world, since Maul2D's world hash now covers each shape's broadphase box and each body's sleep threshold. Queries between steps, character moves among them, see a shape that moved more than 0.1 m in the last step where it is now. A body with a locked rotation no longer turns when given a spin. The 2D step fails at a tick shorter than 1 microsecond or longer than 10 seconds instead of standing still, and the physics statistics count only kinematic targets carried out.