Phase 2.7 review fixes : resetLoggedErrors wiring + IDLE self-heal + JSON cleanup + tests
This commit is contained in:
@@ -37,6 +37,7 @@ import com.tiedup.remake.rig.TiedUpRigConstants;
|
||||
import com.tiedup.remake.rig.event.PatchedRenderersEvent;
|
||||
import com.tiedup.remake.rig.patch.LivingEntityPatch;
|
||||
import com.tiedup.remake.rig.patch.TiedUpCapabilities;
|
||||
import com.tiedup.remake.rig.tick.RigAnimationTickHandler;
|
||||
|
||||
/**
|
||||
* Dispatch renderer RIG — Phase 2.6.
|
||||
@@ -192,6 +193,12 @@ public final class TiedUpRenderEngine {
|
||||
// nouveau crash post-reload. Borne aussi la taille en session longue.
|
||||
loggedRenderErrors.clear();
|
||||
|
||||
// Même raisonnement pour l'anti-spam du tick handler anim : un
|
||||
// UUID ayant crashé pré-reload doit pouvoir à nouveau logger
|
||||
// post-reload. F3+T = point naturel pour resetter tout l'état
|
||||
// log accumulé côté RIG client.
|
||||
RigAnimationTickHandler.resetLoggedErrors();
|
||||
|
||||
// Phase 2 : player seul. Le lambda capture la context finale.
|
||||
entityRendererProvider.put(EntityType.PLAYER, entityType -> new TiedUpPlayerRenderer(context, entityType));
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
package com.tiedup.remake.rig.tick;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -22,6 +21,7 @@ import org.slf4j.Logger;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.rig.TiedUpAnimationRegistry;
|
||||
import com.tiedup.remake.rig.TiedUpRigRegistry;
|
||||
import com.tiedup.remake.rig.anim.Animator;
|
||||
import com.tiedup.remake.rig.anim.LivingMotions;
|
||||
import com.tiedup.remake.rig.anim.client.ClientAnimator;
|
||||
@@ -119,13 +119,21 @@ public final class RigAnimationTickHandler {
|
||||
LOGGED_ERRORS.clear();
|
||||
}
|
||||
|
||||
// Copie défensive : le set level.players() peut muter pendant notre
|
||||
// itération si un joueur join/leave (MP). Le CME-safe serait itérer
|
||||
// sur une copie. On tolère le CME via try/catch, le coût copy n'en
|
||||
// vaut pas la peine pour une liste typiquement petite (~10 joueurs
|
||||
// visibles max).
|
||||
for (Player player : new HashSet<>(mc.level.players())) {
|
||||
tickPlayer(player);
|
||||
// On itère directement level.players() — pas de copie défensive :
|
||||
// le coût d'allocation par tick client (~60 fois/sec) n'est pas
|
||||
// justifié pour une liste typiquement petite (~10 joueurs visibles
|
||||
// max) alors qu'un CME join/leave pendant une frame est déjà attrapé
|
||||
// par le try/catch per-entity dans tickPlayer(). Tradeoff perf vs
|
||||
// correctness : correctness déjà assurée par le catch, le CME est
|
||||
// une anomalie transitoire qui sera resync à la frame suivante.
|
||||
try {
|
||||
for (Player player : mc.level.players()) {
|
||||
tickPlayer(player);
|
||||
}
|
||||
} catch (java.util.ConcurrentModificationException cme) {
|
||||
// Un join/leave MP ou un unload de dimension pendant l'itération.
|
||||
// On skip le reste du tick — la frame suivante repartira propre.
|
||||
// Pas de log : anomalie transitoire attendue (<1/session typique).
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,12 +235,45 @@ public final class RigAnimationTickHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// Self-heal : si le bind IDLE d'un patch (PlayerPatch.addAnimations)
|
||||
// a été construit AVANT que TiedUpAnimationRegistry.initStaticAnimations()
|
||||
// ait tourné, le bind pointe encore sur EMPTY_ANIMATION au lieu de
|
||||
// CONTEXT_STAND_IDLE. On rebind ici — idempotent : les addLivingAnimation
|
||||
// suivantes écrasent simplement la précédente entry dans la map.
|
||||
//
|
||||
// Ce cas se produit si :
|
||||
// - Le patch construit la première fois sur le login (pré-setup)
|
||||
// - Un test runClient démarre avant FMLCommonSetupEvent async enqueueWork
|
||||
// - L'asset CONTEXT_STAND_IDLE est en fallback EMPTY après échec JSON
|
||||
// (registry throw swallowed → CONTEXT_STAND_IDLE reste null cf. le
|
||||
// isReady() guard au-dessus → on n'entre même pas dans ce bloc).
|
||||
//
|
||||
// On utilise getLivingAnimation(motion, defaultGetter) avec null comme
|
||||
// default pour distinguer "pas de bind" d'un bind explicite vers
|
||||
// EMPTY_ANIMATION. Dans les deux cas on rebind.
|
||||
AssetAccessor<? extends StaticAnimation> currentIdleBind =
|
||||
clientAnimator.getLivingAnimation(LivingMotions.IDLE, null);
|
||||
if (currentIdleBind == null || currentIdleBind == TiedUpRigRegistry.EMPTY_ANIMATION) {
|
||||
clientAnimator.addLivingAnimation(LivingMotions.IDLE, target.getAccessor());
|
||||
}
|
||||
|
||||
clientAnimator.playAnimation(target.getAccessor(), 0.2F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset des erreurs loggées — utile aux tests unitaires et au F3+T
|
||||
* reload (aligné avec {@code TiedUpRenderEngine.onAddLayers}).
|
||||
* Reset des erreurs loggées. Appelé :
|
||||
* <ul>
|
||||
* <li>Par les tests unitaires (isolation d'état entre cas)</li>
|
||||
* <li>Par {@code TiedUpRenderEngine.ModBusEvents#onAddLayers} sur chaque
|
||||
* F3+T reload / resource-pack swap. Un UUID ayant crashé pré-reload
|
||||
* reste sinon marqué et masquerait tout nouveau succès / crash
|
||||
* post-reload. Borne aussi la taille du set en session longue.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Thread-safety : {@code LOGGED_ERRORS} est un
|
||||
* {@link ConcurrentHashMap#newKeySet()} — aucun besoin de synchronized
|
||||
* même si appelé depuis un thread loader pendant qu'un tick client est
|
||||
* en flight.</p>
|
||||
*/
|
||||
public static void resetLoggedErrors() {
|
||||
LOGGED_ERRORS.clear();
|
||||
|
||||
Reference in New Issue
Block a user