refactor(03-06): remove debug logs + efficiency pass

Log cleanup:
- Drop redundant INFO "Gravity Flip enabled" in setup() (substantive version at
  start() remains as the single startup line).
- Drop INFO "debug enter/exit notifier ENABLED" (the notifier itself emits per
  region transition; extra startup noise not needed).
- Remove dead back-compat GravityApplier ctors that accepted an infoHandler
  Consumer (no callers post-03-06; debug logs already stripped).
- Clean stale javadoc referencing removed [DBG npc.woken]/[DBG npc.ctrlNull]
  one-shot logs.
- Also ship the pre-staged cleanup work: RegionEnterNotifier gated behind
  GRAVITYFLIP_DEBUG_NOTIFY env var, GravityApplier infoHandler field removed,
  tick logs stripped.

Efficiency:
- RegionVisualizer.resolveParticleId now memoises the requested->resolved
  mapping in a ConcurrentHashMap, eliminating the ParticleSystem.getAssetMap()
  lookup on every tick emission per region. Warn-once semantics preserved via
  warnedInvalidIds. Fail-open path (AssetMap unavailable in tests) intentionally
  does not populate the cache.
- Document in GravityApplier javadoc why Pass 1 + Pass 2 cannot be fused:
  restore requires the complete currentlyInRegion set before diffing against
  previouslyInverted.

Considered but not applied:
- ParticleEdgeEmitter.edgePoints caching per (box, density): throttled to
  >=100ms refresh and typical <20 regions => alloc pressure negligible;
  premature without measurement.
- Reusing RegionRegistry snapshot in RegionEnterNotifier: notifier is
  opt-in/off-by-default, so its independent ECS scan has zero prod cost.

Tests: ./gradlew clean build green, no test changes required.
This commit is contained in:
2026-04-23 16:43:07 +02:00
parent beba729115
commit 1e47f4e846
7 changed files with 219 additions and 54 deletions
@@ -48,10 +48,14 @@ import java.util.function.Consumer;
* filter BEFORE any wake / cmdBuf flip ; {@code VerticalForce} replaces the hardcoded 0.1.</li>
* <li>{@link FallDamageGuard} notified on entry (pass 1) and exit (pass 2) with the
* first-matched region to drive {@link FallDamageSuppressorSystem}.</li>
* <li>Debug throttle logs {@code [DBG tick=...]} removed. One-shot {@code [DBG npc.woken]}
* and {@code [DBG npc.ctrlNull]} preserved — useful when diagnosing new NPC role classes.</li>
* </ul>
*
* <p><b>Hotpath note:</b> Pass 1 (flip) and Pass 2 (restore) each iterate
* {@code forEachEntityParallel}. They CANNOT be fused: restore requires the complete
* {@code currentlyInRegion} set (computed during Pass 1) to diff against
* {@code previouslyInverted}. Merging would either miss restores or double-visit
* entities with inconsistent per-chunk state.
*
* <p><b>Multi-region precedence :</b> for an entity simultaneously inside N regions, the FIRST
* region encountered in the iteration of {@code snapshot.byRegion().keySet()} (Java insertion
* order via the underlying {@code LinkedHashMap}) drives the config values read this tick
@@ -147,23 +151,14 @@ public final class GravityApplier {
private final ConcurrentHashMap<UUID, GravityFlipRegion> lastKnownRegion = new ConcurrentHashMap<>();
private final Consumer<Throwable> errorHandler;
private final Consumer<String> infoHandler;
private final FallDamageGuard guard;
/** One-shot per-UUID log tracking (diagnostic prod) — retained after Plan 03-04 cleanup. */
private final Set<UUID> loggedNpcUuids = ConcurrentHashMap.newKeySet();
public GravityApplier(Consumer<Throwable> errorHandler) {
this(errorHandler, null, new FallDamageGuard());
this(errorHandler, new FallDamageGuard());
}
public GravityApplier(Consumer<Throwable> errorHandler, Consumer<String> infoHandler) {
this(errorHandler, infoHandler, new FallDamageGuard());
}
public GravityApplier(Consumer<Throwable> errorHandler, Consumer<String> infoHandler, FallDamageGuard guard) {
public GravityApplier(Consumer<Throwable> errorHandler, FallDamageGuard guard) {
this.errorHandler = errorHandler == null ? t -> {} : errorHandler;
this.infoHandler = infoHandler == null ? m -> {} : infoHandler;
this.guard = guard == null ? new FallDamageGuard() : guard;
}
@@ -378,20 +373,9 @@ public final class GravityApplier {
if (npc != null) {
try {
Role role = npc.getRole();
if (role == null) {
// rôle non résolu — rien à faire, no-op
} else {
if (role != null) {
MotionController active = role.getActiveMotionController();
if (active == null) {
// log one-shot : rôle non-null mais controller null (utile diag prod)
UUIDComponent uc = null;
try { uc = chunk.getComponent(index, uuidType()); } catch (Throwable ignored) {}
if (uc != null && loggedNpcUuids.add(uc.getUuid())) {
infoHandler.accept(String.format(
"[DBG npc.ctrlNull] uuid=%s roleClass=%s",
uc.getUuid(), role.getClass().getName()));
}
} else {
if (active != null) {
active.updatePhysicsValues(targetValues);
// Plan 03-04 : seed forceVelocity.y paramétré par VerticalForce (remplace
@@ -407,15 +391,6 @@ public final class GravityApplier {
errorHandler.accept(th);
}
}
// log one-shot : première fois qu'on voit cet UUID NPC
UUIDComponent uc = null;
try { uc = chunk.getComponent(index, uuidType()); } catch (Throwable ignored) {}
if (uc != null && loggedNpcUuids.add(uc.getUuid())) {
infoHandler.accept(String.format(
"[DBG npc.woken] uuid=%s controllerClass=%s roleClass=%s targetFlag=%s",
uc.getUuid(), active.getClass().getName(), role.getClass().getName(), targetFlag));
}
}
}
} catch (Throwable th) {