LoVisual/mod/src/main/java/dev/loki/lovisual/mixins/LoVisualMixinPlugin.java
LoVisual AI 135628c2f1
feat(mod): Stage 3 (continued 2) — entity + block + Sodium contract mixins
Per mod/docs/optimize-layer-design.md Stage 3 (continued). This batch
implements the Stage 1 mixin-contract interfaces on their target MC classes
so CachedName, StateEntity, TickCached, CachedLight, CachedValidity and
CachedStack actually get used. Seven new mixin'ов; no new helpers (the
helpers were all written in Stage 1 already).

Entity-contract mixins (mixins/optimize/entity/):
- EntityNameMixin: implements CachedName on Entity — adds the per-tick
  cached name-tag fields (Component + tick). Vanilla Entity.getDisplayName()
  rebuilds the team prefix + name + suffix on every call.
- EntityRenderStateEntityMixin: implements StateEntity on EntityRenderState —
  adds the back-reference field to the source Entity. Render-states are
  rebuilt every frame so they can't keep their own pointer; this is captured
  by EntityRendererStateMixin.
- EntityRendererStateMixin: Inject at RETURN on EntityRenderer.createRenderState
  — captures the source Entity on the freshly-built state via the StateEntity
  contract. ZakoOpt used @ModifyReturnValue; we use plain @Inject because the
  MixinExtras ModifyReturnValue transformer is more fragile across MC versions.
- EntityRendererNameMixin: WrapOperation on EntityRenderer.getNameTag's
  Entity.getDisplayName call — caches by game tick so the per-frame cost is
  one identity check + read instead of a full Component rebuild.
- SodiumEntityRendererMixin: WrapOperation on Sodium EntityRenderer.renderCuboid's
  MemoryStack.stackPush — uses CachedStack (Stage 1) to skip the ThreadLocal
  lookup on the render thread. Registered as optional via LoVisualMixinPlugin
  (only applied when Sodium is loaded); remap = false because the target is
  a Sodium class, not a Mojang one.

BlockEntity-contract mixins (mixins/optimize/block/):
- BlockEntityMixin: implements TickCached + CachedLight + CachedValidity on
  BlockEntity — adds the per-tick cache slot, per-tick block-light lookups
  (single + pair for double-chests), per-tick should-render distance check,
  and per-state validity cache. All four are read by the (Stage 3, future)
  ChestRendererMixin / BlockEntityRenderStateMixin / BlockEntityRenderDispatcherMixin.
- BaseSpawnerMixin: implements TickCached on BaseSpawner — adds the per-tick
  cache slot that SpawnerCache (Stage 1) reads back.

Folder limit (≤4 .java per folder):
- mixins/optimize/entity/ hit 6 files after this batch — split into
  mixins/optimize/entity/renderer/ for EntityRendererStateMixin +
  EntityRendererNameMixin (the two that target EntityRenderer rather than
  Entity/EntityRenderState directly). The other 4 stay in entity/.

lovisual.mixins.json: seven new entries in the 'client' array, alphabetical:
  optimize.block.BaseSpawnerMixin
  optimize.block.BlockEntityMixin
  optimize.entity.EntityNameMixin
  optimize.entity.EntityRenderStateEntityMixin
  optimize.entity.SodiumEntityRendererMixin
  optimize.entity.renderer.EntityRendererNameMixin
  optimize.entity.renderer.EntityRendererStateMixin

LoVisualMixinPlugin: added SodiumEntityRendererMixin to OPTIONAL_SODIUM_MIXINS
so the mixin is only applied when Sodium is loaded (matches the existing
convention for sodium.* mixins).

Tests: no new tests in this batch (the new mixins are pure side-effect
field-bags — they don't have unit-testable logic). Existing suite still green.
Total: 866 tests, 0 failures. checkFolderLimit: OK (0 папок >4).
2026-10-11 10:23:03 +02:00

116 lines
5.2 KiB
Java

package dev.loki.lovisual.mixins;
import net.fabricmc.loader.api.FabricLoader;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import dev.loki.lovisual.util.logging.DebugLog;
import java.util.List;
import java.util.Set;
public final class LoVisualMixinPlugin implements IMixinConfigPlugin {
// Sodium 26.2 uses Mojang RenderPipeline for terrain on both GL and Vulkan backends.
private static final boolean STUB_SODIUM = false;
private static final boolean STUB_IRIS = false;
private static final Set<String> DISABLED_SODIUM_SHADOW_GRAPH_MIXINS = Set.of();
private static final Set<String> OPTIONAL_SODIUM_MIXINS = Set.of(
"dev.loki.lovisual.mixins.sodium.terrain.SodiumBlockRendererMixin",
"dev.loki.lovisual.mixins.sodium.terrain.SodiumChunkMeshFormatsMixin",
"dev.loki.lovisual.mixins.sodium.vertex.SodiumChunkVertexMixin",
"dev.loki.lovisual.mixins.sodium.terrain.SodiumShaderChunkRendererMixin",
"dev.loki.lovisual.mixins.sodium.vertex.SodiumVertexConsumerTrackerMixin",
"dev.loki.lovisual.mixins.sodium.placement.SodiumPlaceCullMixin",
"dev.loki.lovisual.mixins.sodium.placement.SodiumPlaceHideMixin",
"dev.loki.lovisual.mixins.optimize.entity.SodiumEntityRendererMixin"
);
private static final Set<String> OPTIONAL_IRIS_MIXINS = Set.of(
"dev.loki.lovisual.mixins.iris.render.IrisCommonUniformsMixin",
"dev.loki.lovisual.mixins.iris.render.IrisGameRendererInteropMixin",
"dev.loki.lovisual.mixins.iris.hand.IrisHandRendererAccessor",
"dev.loki.lovisual.mixins.iris.hand.IrisHandRendererMixin",
"dev.loki.lovisual.mixins.iris.processor.IrisIncludeProcessorMixin",
"dev.loki.lovisual.mixins.iris.render.IrisShaderPackLoadMixin",
"dev.loki.lovisual.mixins.iris.render.IrisRenderingPipelineFinalizeMixin",
"dev.loki.lovisual.mixins.iris.hand.IrisVanillaHandInteropMixin"
);
private static final Set<String> DISABLED_WITH_IRIS_MIXINS = Set.of(
"dev.loki.lovisual.mixins.sodium.terrain.SodiumBlockRendererMixin",
"dev.loki.lovisual.mixins.sodium.terrain.SodiumChunkMeshFormatsMixin",
"dev.loki.lovisual.mixins.sodium.vertex.SodiumChunkVertexMixin",
"dev.loki.lovisual.mixins.sodium.terrain.SodiumShaderChunkRendererMixin"
);
private static final Set<String> OPTIONAL_XAERO_MINIMAP_MIXINS = Set.of(
"dev.loki.lovisual.mixins.xaero.minimap.AbstractWaypointRenderProviderMixin"
);
private static final Set<String> OPTIONAL_XAERO_WORLDMAP_MIXINS = Set.of(
"dev.loki.lovisual.mixins.xaero.worldmap.WaypointRenderProviderMixin"
);
private static final Set<String> OPTIONAL_MORECULLING_MIXINS = Set.of(
"dev.loki.lovisual.mixins.moreculling.CullingUtilsMixin"
);
private static volatile boolean loggedSodiumDecision;
@Override
public void onLoad(String mixinPackage) {
}
@Override
public String getRefMapperConfig() {
return null;
}
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
if (STUB_SODIUM && OPTIONAL_SODIUM_MIXINS.contains(mixinClassName)) {
return false;
}
if (STUB_IRIS && OPTIONAL_IRIS_MIXINS.contains(mixinClassName)) {
return false;
}
if (DISABLED_SODIUM_SHADOW_GRAPH_MIXINS.contains(mixinClassName)) {
return false;
}
if (OPTIONAL_XAERO_MINIMAP_MIXINS.contains(mixinClassName)) {
return FabricLoader.getInstance().isModLoaded("xaerominimap");
}
if (OPTIONAL_XAERO_WORLDMAP_MIXINS.contains(mixinClassName)) {
return FabricLoader.getInstance().isModLoaded("xaeroworldmap");
}
if (OPTIONAL_MORECULLING_MIXINS.contains(mixinClassName)) {
return FabricLoader.getInstance().isModLoaded("moreculling");
}
if (OPTIONAL_IRIS_MIXINS.contains(mixinClassName)) {
return FabricLoader.getInstance().isModLoaded("iris");
}
if (DISABLED_WITH_IRIS_MIXINS.contains(mixinClassName)) {
return !FabricLoader.getInstance().isModLoaded("iris");
}
if (!OPTIONAL_SODIUM_MIXINS.contains(mixinClassName)) {
return true;
}
boolean sodiumLoaded = FabricLoader.getInstance().isModLoaded("sodium");
if (!loggedSodiumDecision) {
loggedSodiumDecision = true;
DebugLog.warn("[SodiumCompat] optional mixins %s", sodiumLoaded ? "enabled" : "disabled");
}
return sodiumLoaded;
}
@Override
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {
}
@Override
public List<String> getMixins() {
return null;
}
@Override
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
}
@Override
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
}
}