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).
This commit is contained in:
parent
15f070af6d
commit
135628c2f1
9 changed files with 415 additions and 1 deletions
|
|
@ -21,7 +21,8 @@ public final class LoVisualMixinPlugin implements IMixinConfigPlugin {
|
||||||
"dev.loki.lovisual.mixins.sodium.terrain.SodiumShaderChunkRendererMixin",
|
"dev.loki.lovisual.mixins.sodium.terrain.SodiumShaderChunkRendererMixin",
|
||||||
"dev.loki.lovisual.mixins.sodium.vertex.SodiumVertexConsumerTrackerMixin",
|
"dev.loki.lovisual.mixins.sodium.vertex.SodiumVertexConsumerTrackerMixin",
|
||||||
"dev.loki.lovisual.mixins.sodium.placement.SodiumPlaceCullMixin",
|
"dev.loki.lovisual.mixins.sodium.placement.SodiumPlaceCullMixin",
|
||||||
"dev.loki.lovisual.mixins.sodium.placement.SodiumPlaceHideMixin"
|
"dev.loki.lovisual.mixins.sodium.placement.SodiumPlaceHideMixin",
|
||||||
|
"dev.loki.lovisual.mixins.optimize.entity.SodiumEntityRendererMixin"
|
||||||
);
|
);
|
||||||
private static final Set<String> OPTIONAL_IRIS_MIXINS = Set.of(
|
private static final Set<String> OPTIONAL_IRIS_MIXINS = Set.of(
|
||||||
"dev.loki.lovisual.mixins.iris.render.IrisCommonUniformsMixin",
|
"dev.loki.lovisual.mixins.iris.render.IrisCommonUniformsMixin",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package dev.loki.lovisual.mixins.optimize.block;
|
||||||
|
|
||||||
|
import dev.loki.lovisual.features.module.modules.misc.optimize.block.TickCached;
|
||||||
|
import net.minecraft.world.level.BaseSpawner;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage 3: implements {@link TickCached} on {@link BaseSpawner} — adds the
|
||||||
|
* per-tick cache slot that {@link dev.loki.lovisual.features.module.modules.misc.optimize.block.SpawnerCache}
|
||||||
|
* reads back to skip the per-frame {@code extractEntity} rebuild of the
|
||||||
|
* spawner's display mob.
|
||||||
|
*
|
||||||
|
* <p>The vanilla {@code SpawnerRenderer.extractRenderState} rebuilds the
|
||||||
|
* display entity's render state every frame for every spawner in view;
|
||||||
|
* the display entity only changes on tick boundaries (spin speed, mob
|
||||||
|
* rotation, etc.), so caching by tick removes that rebuild from the hot path.
|
||||||
|
*
|
||||||
|
* <p>Ported from {@code zako.opt.mixin.block.BaseSpawnerMixin} (ZakoOpt,
|
||||||
|
* LGPL-3.0). Original author: Zako — https://t.me/StarikZako. Field/method
|
||||||
|
* prefix changed from {@code zakoopt$} to {@code lovisual$}.
|
||||||
|
*/
|
||||||
|
@Mixin(BaseSpawner.class)
|
||||||
|
public abstract class BaseSpawnerMixin implements TickCached {
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private Object lovisual$cached;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private long lovisual$cachedTick = Long.MIN_VALUE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object lovisual$cached() {
|
||||||
|
return lovisual$cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long lovisual$cachedTick() {
|
||||||
|
return lovisual$cachedTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lovisual$setCached(Object value, long tick) {
|
||||||
|
lovisual$cached = value;
|
||||||
|
lovisual$cachedTick = tick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
package dev.loki.lovisual.mixins.optimize.block;
|
||||||
|
|
||||||
|
import dev.loki.lovisual.features.module.modules.misc.optimize.block.CachedLight;
|
||||||
|
import dev.loki.lovisual.features.module.modules.misc.optimize.block.CachedValidity;
|
||||||
|
import dev.loki.lovisual.features.module.modules.misc.optimize.block.TickCached;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage 3: implements {@link TickCached}, {@link CachedLight} and
|
||||||
|
* {@link CachedValidity} on {@link BlockEntity} — adds the per-tick cache
|
||||||
|
* slot (object + tick), the per-tick block-light lookups, the per-tick
|
||||||
|
* should-render distance check, and the per-state validity cache.
|
||||||
|
*
|
||||||
|
* <p>The vanilla {@code BlockEntityType.isValid(state)} is called every
|
||||||
|
* frame per block-entity in view but only changes on a state transition;
|
||||||
|
* {@code LightCoordsUtil.getLightCoords(level, pos)} walks the chunk's light
|
||||||
|
* section data per call; {@code BlockEntityRenderer.shouldRender} does a
|
||||||
|
* distance check that only changes when the entity moves across the cull
|
||||||
|
* boundary. All three are cached via the interface fields implemented here.
|
||||||
|
*
|
||||||
|
* <p>Ported from {@code zako.opt.mixin.block.BlockEntityMixin} (ZakoOpt,
|
||||||
|
* LGPL-3.0). Original author: Zako — https://t.me/StarikZako. Field/method
|
||||||
|
* prefix changed from {@code zakoopt$} to {@code lovisual$}.
|
||||||
|
*/
|
||||||
|
@Mixin(BlockEntity.class)
|
||||||
|
public abstract class BlockEntityMixin implements TickCached, CachedLight, CachedValidity {
|
||||||
|
|
||||||
|
// --- TickCached ---
|
||||||
|
@Unique
|
||||||
|
private Object lovisual$cached;
|
||||||
|
@Unique
|
||||||
|
private long lovisual$cachedTick = Long.MIN_VALUE;
|
||||||
|
|
||||||
|
// --- CachedLight ---
|
||||||
|
@Unique
|
||||||
|
private int lovisual$light;
|
||||||
|
@Unique
|
||||||
|
private long lovisual$lightTick = Long.MIN_VALUE;
|
||||||
|
@Unique
|
||||||
|
private int lovisual$pairLight;
|
||||||
|
@Unique
|
||||||
|
private long lovisual$pairLightTick = Long.MIN_VALUE;
|
||||||
|
|
||||||
|
// --- CachedValidity ---
|
||||||
|
@Unique
|
||||||
|
private BlockState lovisual$validState;
|
||||||
|
@Unique
|
||||||
|
private boolean lovisual$valid;
|
||||||
|
@Unique
|
||||||
|
private long lovisual$distanceTick = Long.MIN_VALUE;
|
||||||
|
@Unique
|
||||||
|
private boolean lovisual$inDistance;
|
||||||
|
|
||||||
|
// --- TickCached ---
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object lovisual$cached() {
|
||||||
|
return lovisual$cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long lovisual$cachedTick() {
|
||||||
|
return lovisual$cachedTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lovisual$setCached(Object value, long tick) {
|
||||||
|
lovisual$cached = value;
|
||||||
|
lovisual$cachedTick = tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CachedLight ---
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int lovisual$light() {
|
||||||
|
return lovisual$light;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long lovisual$lightTick() {
|
||||||
|
return lovisual$lightTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lovisual$setLight(int light, long tick) {
|
||||||
|
lovisual$light = light;
|
||||||
|
lovisual$lightTick = tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int lovisual$pairLight() {
|
||||||
|
return lovisual$pairLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long lovisual$pairLightTick() {
|
||||||
|
return lovisual$pairLightTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lovisual$setPairLight(int light, long tick) {
|
||||||
|
lovisual$pairLight = light;
|
||||||
|
lovisual$pairLightTick = tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CachedValidity ---
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState lovisual$validState() {
|
||||||
|
return lovisual$validState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean lovisual$valid() {
|
||||||
|
return lovisual$valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lovisual$setValid(BlockState state, boolean valid) {
|
||||||
|
lovisual$validState = state;
|
||||||
|
lovisual$valid = valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long lovisual$distanceTick() {
|
||||||
|
return lovisual$distanceTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean lovisual$inDistance() {
|
||||||
|
return lovisual$inDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lovisual$setInDistance(boolean inDistance, long tick) {
|
||||||
|
lovisual$inDistance = inDistance;
|
||||||
|
lovisual$distanceTick = tick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package dev.loki.lovisual.mixins.optimize.entity;
|
||||||
|
|
||||||
|
import dev.loki.lovisual.features.module.modules.misc.optimize.entity.CachedName;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage 3: implements {@link CachedName} on {@link Entity} — adds the cached
|
||||||
|
* name-tag fields (name + tick) that {@link EntityRendererNameMixin} populates
|
||||||
|
* and reads back. Vanilla {@code Entity.getDisplayName()} rebuilds the team
|
||||||
|
* prefix + name + suffix on every call; renderers call it once per frame per
|
||||||
|
* entity in view, so caching by tick cuts the per-frame cost.
|
||||||
|
*
|
||||||
|
* <p>Ported from {@code zako.opt.mixin.entity.EntityNameMixin} (ZakoOpt,
|
||||||
|
* LGPL-3.0). Original author: Zako — https://t.me/StarikZako. Field/method
|
||||||
|
* prefix changed from {@code zakoopt$} to {@code lovisual$}.
|
||||||
|
*/
|
||||||
|
@Mixin(Entity.class)
|
||||||
|
public abstract class EntityNameMixin implements CachedName {
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private Component lovisual$name;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private long lovisual$nameTick = Long.MIN_VALUE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component lovisual$name() {
|
||||||
|
return lovisual$name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long lovisual$nameTick() {
|
||||||
|
return lovisual$nameTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lovisual$setName(Component name, long tick) {
|
||||||
|
lovisual$name = name;
|
||||||
|
lovisual$nameTick = tick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package dev.loki.lovisual.mixins.optimize.entity;
|
||||||
|
|
||||||
|
import dev.loki.lovisual.features.module.modules.misc.optimize.entity.StateEntity;
|
||||||
|
import net.minecraft.client.renderer.entity.state.EntityRenderState;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage 3: implements {@link StateEntity} on {@link EntityRenderState} — adds
|
||||||
|
* the back-reference field to the source {@link Entity}. Render-states are
|
||||||
|
* rebuilt every frame, so they can't keep their own pointer — the
|
||||||
|
* {@link EntityRendererStateMixin} captures it once per state construction
|
||||||
|
* via {@code createRenderState} and downstream renderers read it back.
|
||||||
|
*
|
||||||
|
* <p>Ported from {@code zako.opt.mixin.entity.EntityRenderStateEntityMixin}
|
||||||
|
* (ZakoOpt, LGPL-3.0). Original author: Zako — https://t.me/StarikZako.
|
||||||
|
* Prefix changed from {@code zakoopt$} to {@code lovisual$}.
|
||||||
|
*/
|
||||||
|
@Mixin(EntityRenderState.class)
|
||||||
|
public abstract class EntityRenderStateEntityMixin implements StateEntity {
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private Entity lovisual$entity;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entity lovisual$entity() {
|
||||||
|
return lovisual$entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lovisual$entity(Entity entity) {
|
||||||
|
lovisual$entity = entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package dev.loki.lovisual.mixins.optimize.entity;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||||
|
import dev.loki.lovisual.features.module.modules.misc.optimize.entity.CachedStack;
|
||||||
|
import dev.loki.lovisual.render.engine.optimize.OptimizeToggles;
|
||||||
|
import org.lwjgl.system.MemoryStack;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage 3: hot-path helper for Sodium's entity cuboid renderer — wraps
|
||||||
|
* {@code MemoryStack.stackPush()} so the render thread's stack is cached by
|
||||||
|
* identity instead of being looked up via ThreadLocal on every cuboid. Sodium
|
||||||
|
* calls this once per cuboid per frame (thousands of times for dense entity
|
||||||
|
* scenes), so the cached lookup is a measurable win on the hot path.
|
||||||
|
*
|
||||||
|
* <p>Uses {@code WrapOperation} rather than {@code @Redirect} because
|
||||||
|
* MixinExtras 0.5.4 (which Fabric Loader 0.19.3 ships with) crashes on a
|
||||||
|
* static {@code @Redirect} here — pinned to 0.5.5 in {@code build.gradle},
|
||||||
|
* but the convention is kept for safety. The mixin is registered with
|
||||||
|
* {@code remap = false} because the target is a Sodium class, not a Mojang one.
|
||||||
|
*
|
||||||
|
* <p>Ported from {@code zako.opt.mixin.entity.SodiumEntityRendererMixin}
|
||||||
|
* (ZakoOpt, LGPL-3.0). Original author: Zako — https://t.me/StarikZako.
|
||||||
|
* Gated on the {@code microOpts} toggle via {@link OptimizeToggles#microOpts()}.
|
||||||
|
* When the toggle is off, the original runs unchanged.
|
||||||
|
*
|
||||||
|
* <p>Registered conditionally via {@code LoVisualMixinPlugin} — only applied
|
||||||
|
* when Sodium is loaded. See {@code lovisual.mixins.json} entry in the
|
||||||
|
* {@code client} array.
|
||||||
|
*/
|
||||||
|
@Mixin(targets = "net.caffeinemc.mods.sodium.client.render.immediate.model.EntityRenderer", remap = false)
|
||||||
|
public abstract class SodiumEntityRendererMixin {
|
||||||
|
|
||||||
|
@WrapOperation(
|
||||||
|
method = "renderCuboid",
|
||||||
|
at = @At(value = "INVOKE",
|
||||||
|
target = "Lorg/lwjgl/system/MemoryStack;stackPush()Lorg/lwjgl/system/MemoryStack;")
|
||||||
|
)
|
||||||
|
private static MemoryStack lovisual$cachedStack(Operation<MemoryStack> original) {
|
||||||
|
return OptimizeToggles.microOpts() ? CachedStack.push() : original.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package dev.loki.lovisual.mixins.optimize.entity.renderer;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||||
|
import dev.loki.lovisual.features.module.modules.misc.optimize.entity.CachedName;
|
||||||
|
import dev.loki.lovisual.render.engine.optimize.OptimizeToggles;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage 3: caches {@code Entity.getDisplayName()} per tick. The vanilla
|
||||||
|
* implementation rebuilds a {@link Component} (team prefix + name + suffix)
|
||||||
|
* every call; renderers call it once per frame per entity in view. Wrapping
|
||||||
|
* the call here keeps the per-frame cost to one identity check + read,
|
||||||
|
* instead of a full Component rebuild + style lookup.
|
||||||
|
*
|
||||||
|
* <p>The cache is keyed by game tick — name parts only change on tick
|
||||||
|
* boundaries (team membership, name change). On the first call of a tick the
|
||||||
|
* original runs and the result is stored; subsequent calls in the same tick
|
||||||
|
* return the cached instance.
|
||||||
|
*
|
||||||
|
* <p>Ported from {@code zako.opt.mixin.entity.EntityRendererNameMixin}
|
||||||
|
* (ZakoOpt, LGPL-3.0). Original author: Zako — https://t.me/StarikZako.
|
||||||
|
* Gated on the {@code microOpts} toggle via {@link OptimizeToggles#microOpts()}.
|
||||||
|
* When the toggle is off, the original runs unchanged.
|
||||||
|
*/
|
||||||
|
@Mixin(EntityRenderer.class)
|
||||||
|
public abstract class EntityRendererNameMixin {
|
||||||
|
|
||||||
|
@WrapOperation(
|
||||||
|
method = "getNameTag",
|
||||||
|
at = @At(value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/world/entity/Entity;getDisplayName()Lnet/minecraft/network/chat/Component;")
|
||||||
|
)
|
||||||
|
private Component lovisual$cachedName(Entity entity, Operation<Component> original) {
|
||||||
|
if (!OptimizeToggles.microOpts()) {
|
||||||
|
return original.call(entity);
|
||||||
|
}
|
||||||
|
if (!(entity instanceof CachedName cache)) {
|
||||||
|
return original.call(entity);
|
||||||
|
}
|
||||||
|
long tick = entity.level().getGameTime();
|
||||||
|
if (cache.lovisual$nameTick() != tick) {
|
||||||
|
cache.lovisual$setName(original.call(entity), tick);
|
||||||
|
}
|
||||||
|
return cache.lovisual$name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package dev.loki.lovisual.mixins.optimize.entity.renderer;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import dev.loki.lovisual.features.module.modules.misc.optimize.entity.StateEntity;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||||
|
import net.minecraft.client.renderer.entity.state.EntityRenderState;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stage 3: captures the source {@link Entity} on every freshly-built
|
||||||
|
* {@link EntityRenderState}. Vanilla's {@code createRenderState(entity, partial)}
|
||||||
|
* returns a state without back-reference; this inject at RETURN stores the
|
||||||
|
* entity on the state via the {@link StateEntity} contract so downstream
|
||||||
|
* renderers (model feature renderer, item feature renderer, future LOD mixins)
|
||||||
|
* can reach the live entity.
|
||||||
|
*
|
||||||
|
* <p>Ported from {@code zako.opt.mixin.entity.EntityRendererStateMixin}
|
||||||
|
* (ZakoOpt, LGPL-3.0). Original author: Zako — https://t.me/StarikZako.
|
||||||
|
* The original used {@code @ModifyReturnValue}; LoVisual uses {@code @Inject}
|
||||||
|
* at RETURN with {@code CallbackInfoReturnable} so the mixin stays simple and
|
||||||
|
* doesn't rely on the (more fragile across versions) MixinExtras return-value
|
||||||
|
* transformer — the captured state is set as a side effect, not returned.
|
||||||
|
*/
|
||||||
|
@Mixin(EntityRenderer.class)
|
||||||
|
public abstract class EntityRendererStateMixin {
|
||||||
|
|
||||||
|
@Inject(
|
||||||
|
method = "createRenderState(Lnet/minecraft/world/entity/Entity;F)Lnet/minecraft/client/renderer/entity/state/EntityRenderState;",
|
||||||
|
at = @At("RETURN")
|
||||||
|
)
|
||||||
|
private void lovisual$rememberEntity(Entity entity, float partialTick,
|
||||||
|
CallbackInfoReturnable<EntityRenderState> cir,
|
||||||
|
@Local(argsOnly = true) Entity localEntity) {
|
||||||
|
EntityRenderState state = cir.getReturnValue();
|
||||||
|
if (state instanceof StateEntity stateEntity) {
|
||||||
|
stateEntity.lovisual$entity(localEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,14 @@
|
||||||
"world.level.LevelMixin"
|
"world.level.LevelMixin"
|
||||||
],
|
],
|
||||||
"client": [
|
"client": [
|
||||||
|
"optimize.block.BaseSpawnerMixin",
|
||||||
|
"optimize.block.BlockEntityMixin",
|
||||||
|
"optimize.entity.EntityNameMixin",
|
||||||
|
"optimize.entity.EntityRenderStateEntityMixin",
|
||||||
"optimize.entity.LivingEntityLodMixin",
|
"optimize.entity.LivingEntityLodMixin",
|
||||||
|
"optimize.entity.SodiumEntityRendererMixin",
|
||||||
|
"optimize.entity.renderer.EntityRendererNameMixin",
|
||||||
|
"optimize.entity.renderer.EntityRendererStateMixin",
|
||||||
"optimize.gl.GlCommandEncoderClearMixin",
|
"optimize.gl.GlCommandEncoderClearMixin",
|
||||||
"optimize.gl.GlCommandEncoderTexBufferMixin",
|
"optimize.gl.GlCommandEncoderTexBufferMixin",
|
||||||
"optimize.gl.RenderTypesMixin",
|
"optimize.gl.RenderTypesMixin",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue