This mod was created with the assistance of Claude (Anthropic). このModはAnthropic社のAIモデル「Claude」の支援を受けて作成されました。
Fantasy Ending's EntityASMUtil.getHealthDelta(LivingEntity) acquires a SynchedEntityData ReadWriteLock on every single call. This method is called from nearly every LivingEntity#getHealth() call in the game (redirected there via ASM, through special_getHealth), as well as from special_getHealthDelta / special_isAlive / special_isDeadOrDying. Because health bars, nameplates, and other rendering code call getHealth() extremely often, this added up to a non-trivial cost on the render thread.
This mod is an external Mixin patch — it does not modify Fantasy Ending's own source at all. It works by:
LivingEntity (HealthDeltaEC)Entity#onSyncedDataUpdated (the common callback fired for both local writes and incoming network sync packets) to invalidate the cache only when FE_GET_HEALTH_DATA actually changesEntityASMUtil#getHealthDelta so that, when the cache is valid, the lock-based read is skipped entirely and the cached value is returned immediatelyFE_GET_HEALTH_DATA is network-synced data. On the client, its value can change purely from an incoming sync packet — without setHealthDelta ever being called locally (e.g. when another player's or mob's health is synced to you). Relying on setHealthDelta alone would miss that path and leave stale cached values on the client, so this mod hooks vanilla Entity#onSyncedDataUpdated instead, which fires for both paths. Note that in Minecraft 1.20.1, onSyncedDataUpdated has two overloads (EntityDataAccessor<?> for local writes, List<SynchedEntityData.DataValue<?>> for batched network sync) — both are hooked explicitly by full method descriptor to avoid Mixin's ambiguous by-name resolution.
special_isAlive / special_isDeadOrDying also call getHealthDelta internally, so they benefit from this cache automatically. The same invalidation logic applies, but please report anything that looks off around health-based alive/dead checks.getHealthDelta. Other costs found during profiling (PostProcessingShaders#renderShaders/canUse, GoetyRevelationFix's ClientEventsHandler#drawTrails) are out of scope for this mod.Fantasy Ending の EntityASMUtil.getHealthDelta(LivingEntity) は、呼ばれるたびに SynchedEntityDataのReadWriteLockを取得してから値を読みに行く実装になっています。 このメソッドはASMによってLivingEntity#getHealth()のほぼ全呼び出し(special_getHealth 経由)や、special_getHealthDelta/special_isAlive/special_isDeadOrDyingからも 呼ばれるため、体力バーやネームプレート表示などRender thread側の描画コードから 極めて高頻度に叩かれ、無視できないコストになっていました。
このModは Fantasy Ending本体のソースは一切書き換えず、外付けのMixinパッチとして
LivingEntityにdelta値のキャッシュ用フィールドを追加 (HealthDeltaEC)Entity#onSyncedDataUpdated(ローカルでの書き込み・ネットワーク同期パケット受信の どちらでも呼ばれる共通フック)を検知して、FE_GET_HEALTH_DATAが実際に更新された 時だけキャッシュを無効化するEntityASMUtil#getHealthDeltaにMixinを差し込み、キャッシュが有効なら ReadWriteLockを伴う読み取りそのものをスキップして即座にキャッシュ値を返すという形で実現しています。
FE_GET_HEALTH_DATAはネットワーク同期対象のデータです。クライアント側では、 setHealthDeltaをローカルで一度も呼ばなくても、サーバーからの同期パケット受信 だけでこの値が書き換わるケースがあります(他プレイヤーやMobの体力表示など)。 setHealthDeltaの呼び出しだけをトリガーにすると、このケースを取りこぼして クライアント側で古い値が表示され続けるバグになるため、あえて vanilla の Entity#onSyncedDataUpdated側で検知する設計にしています。1.20.1では onSyncedDataUpdatedに2つのオーバーロード(ローカル書き込み用の EntityDataAccessor<?>版、ネットワーク同期パケットの一括反映用の List<SynchedEntityData.DataValue<?>>版)が存在するため、Mixinの あいまいな名前解決を避けるべく両方とも完全なディスクリプタ指定でフックしています。
special_isAlive/special_isDeadOrDyingも内部でgetHealthDeltaを呼んでいるため、 今回のキャッシュの恩恵を自動的に受けます。無効化ロジックは同じなので基本問題ない はずですが、体力に応じた生死判定まわりで挙動がおかしいと感じたら教えてください。getHealthDelta一点に絞っています。プロファイルで見つかった PostProcessingShaders#renderShaders/canUseやClientEventsHandler#drawTrails (GoetyRevelationFix側)は今回のパッチ範囲外です。