feat(03-02): wake-up MovementManager + MotionController on region flip

- GravityApplier: branches player (setDefaultSettings + applyDefaultSettings + update) et NPC (Role.getActiveMotionController().updatePhysicsValues) dans les 2 pass
- wakePlayerOrNpc() helper appelé depuis forEachEntityParallel (sous world.execute — WorldThread assert OK)
- PhysicsValues construit localement via buildPhysicsValuesWithFlag (évite relookup ECS pre-commit cmdBuf)
- Seam pure FlaggedDecision extraite pour tests unitaires hors runtime Hytale (Rule 3 — static init PhysicsValues nécessite PluginBase)
- 8 tests verts (6 existants + 2 nouveaux sur buildFlaggedDecision)
This commit is contained in:
2026-04-23 12:29:00 +02:00
parent 4a02ceaa82
commit 210c93aee7
2 changed files with 187 additions and 10 deletions
@@ -77,4 +77,26 @@ class GravityApplierDiffTest {
Set<UUID> view = applier.previouslyInvertedView();
assertThrows(UnsupportedOperationException.class, () -> view.add(UUID.randomUUID()));
}
// NOTE (Rule 3 deviation — Plan 03-02) : les tests suivants ciblent la seam pure
// `buildFlaggedDecision(double, double, boolean)` au lieu de `buildPhysicsValuesWithFlag`
// parce que le static init de `PhysicsValues` déclenche un `ExceptionInInitializerError`
// hors runtime Hytale (dépendance ModuleRegistry). La décomposition pure garantit la
// sémantique attendue (mass/drag préservés, flag = target) sans couplage ECS.
@Test
void buildFlaggedDecisionPreservesMassAndDrag() {
GravityApplier.FlaggedDecision out = GravityApplier.buildFlaggedDecision(1.5, 0.7, true);
assertEquals(1.5, out.mass, 1e-9);
assertEquals(0.7, out.drag, 1e-9);
assertTrue(out.invertedGravity);
}
@Test
void buildFlaggedDecisionIsIdempotentWhenAlreadyTarget() {
GravityApplier.FlaggedDecision out = GravityApplier.buildFlaggedDecision(2.0, 0.3, true);
assertEquals(2.0, out.mass, 1e-9);
assertEquals(0.3, out.drag, 1e-9);
assertTrue(out.invertedGravity);
}
}