I'm not even sure whether this code from 1.12.2 will work (if not work, i will have to learn bci, which would be terrible)
package com.github.patrick.customentity.renderer.asm;
import com.github.patrick.customentity.client.CustomEntity;
import com.github.patrick.customentity.client.CustomEntityEvent;
import com.github.patrick.customentity.renderer.CustomRenderer;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.client.renderer.entity.GiantZombieRenderer;
import net.minecraft.client.renderer.entity.LivingRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.FieldVisitor;
import net.minecraft.client.Minecraft;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
@SuppressWarnings("rawtypes")
public class ASMRenderer implements Opcodes {
private static final Field SHADOW_SIZE;
private static final Method PRE_RENDER_CALL_BACK;
private static final ASMClassLoader CLASS_LOADER;
private static int count;
static {
CLASS_LOADER = new ASMClassLoader();
PRE_RENDER_CALL_BACK = getPreRenderCallback();
SHADOW_SIZE = getShadowSize();
}
public static LivingRenderer createRenderClass(final Class<? extends Entity> entityClass, final LivingRenderer render) throws Exception {
final Class<?> superClass = render.getClass();
final String className = "com/github/patrick/customentity/renderer/" + superClass.getSimpleName() + "Custom" + count++;
final ClassWriter writer = new ClassWriter(0);
writer.visit(V1_6, ACC_PUBLIC + ACC_SUPER, className, null, Type.getInternalName(superClass), new String[] { Type.getInternalName(CustomRenderer.class) });
writer.visitSource(".dynamic", null);
final FieldVisitor fv = writer.visitField(ACC_PRIVATE + ACC_FINAL, "defaultShadowSize", "F", null, null);
fv.visitEnd();
int stack = 0;
Constructor<?> con;
if (GiantZombieRenderer.class.isAssignableFrom(superClass)) {
con = findConstructor(superClass, EntityRendererManager.class, Float.TYPE);
stack = 1;
}
else con = findConstructor(superClass, EntityRendererManager.class);
if (con == null) return null;
MethodVisitor visitor = writer.visitMethod(ACC_PUBLIC, "<init>", "(" + Type.getDescriptor(EntityRendererManager.class) + ")V", null, null);
visitor.visitCode();
visitor.visitVarInsn(ALOAD, 0);
visitor.visitVarInsn(ALOAD, 1);
if (stack == 1) visitor.visitLdcInsn(6F);
visitor.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(superClass), "<init>", Type.getConstructorDescriptor(con), false);
visitor.visitVarInsn(ALOAD, 0);
visitor.visitVarInsn(ALOAD, 0);
visitor.visitFieldInsn(GETFIELD, Type.getInternalName(superClass), SHADOW_SIZE.getName(), "F");
visitor.visitFieldInsn(PUTFIELD, className, "defaultShadowSize", "F");
visitor.visitInsn(RETURN);
visitor.visitMaxs(2 + stack, 2);
visitor.visitEnd();
final Label ifLabel = new Label();
final Method preRenderCallback = findPreRenderCallback(entityClass, superClass);
if (preRenderCallback == null) return null;
visitor = writer.visitMethod(ACC_PUBLIC, preRenderCallback.getName(), "(" + Type.getDescriptor(entityClass) + "F)V", null, null);
visitor.visitCode();
if (preRenderCallback.getDeclaringClass() != LivingRenderer.class) {
visitor.visitVarInsn(ALOAD, 0);
visitor.visitVarInsn(ALOAD, 1);
visitor.visitVarInsn(FLOAD, 2);
visitor.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(superClass), preRenderCallback.getName(), Type.getMethodDescriptor(preRenderCallback), false);
}
visitor.visitFieldInsn(GETSTATIC, Type.getInternalName(CustomEntityEvent.class), "rendering", Type.getDescriptor(CustomEntity.class));
visitor.visitVarInsn(ASTORE, 3);
visitor.visitVarInsn(ALOAD, 0);
visitor.visitFieldInsn(GETFIELD, className, "defaultShadowSize", "F");
visitor.visitVarInsn(FSTORE, 4);
visitor.visitVarInsn(ALOAD, 3);
visitor.visitJumpInsn(IFNULL, ifLabel);
visitor.visitVarInsn(FLOAD, 4);
visitor.visitVarInsn(ALOAD, 3);
visitor.visitVarInsn(ALOAD, 1);
visitor.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(CustomEntity.class), "applyGraphic", "(" + Type.getDescriptor(LivingEntity.class) + ")F", false);
visitor.visitInsn(FMUL);
visitor.visitVarInsn(FSTORE, 4);
visitor.visitLabel(ifLabel);
visitor.visitLineNumber(33, ifLabel);
visitor.visitFrame(1, 2, new Object[] { Type.getInternalName(CustomEntity.class), FLOAT }, 0, null);
visitor.visitVarInsn(ALOAD, 0);
visitor.visitVarInsn(FLOAD, 4);
visitor.visitFieldInsn(PUTFIELD, className, SHADOW_SIZE.getName(), "F");
visitor.visitInsn(RETURN);
visitor.visitMaxs(3, 5);
visitor.visitEnd();
visitor = writer.visitMethod(ACC_PUBLIC, "setShadowSize", "(F)V", null, null);
visitor.visitCode();
visitor.visitVarInsn(ALOAD, 0);
visitor.visitVarInsn(FLOAD, 1);
visitor.visitFieldInsn(PUTFIELD, className, SHADOW_SIZE.getName(), "F");
visitor.visitInsn(RETURN);
visitor.visitMaxs(2, 2);
visitor.visitEnd();
if (preRenderCallback.getParameterTypes()[0] != entityClass) {
visitor = writer.visitMethod(ACC_PROTECTED + ACC_VOLATILE + ACC_SYNTHETIC, PRE_RENDER_CALL_BACK.getName(), Type.getMethodDescriptor(preRenderCallback), null, null);
visitor.visitCode();
visitor.visitVarInsn(ALOAD, 0);
visitor.visitVarInsn(ALOAD, 1);
visitor.visitTypeInsn(CHECKCAST, Type.getInternalName(entityClass));
visitor.visitVarInsn(FLOAD, 2);
visitor.visitMethodInsn(INVOKEVIRTUAL, className, PRE_RENDER_CALL_BACK.getName(), "(" + Type.getDescriptor(entityClass) + "F)V", false);
visitor.visitInsn(RETURN);
visitor.visitMaxs(3, 3);
visitor.visitEnd();
}
writer.visitEnd();
return (LivingRenderer) CLASS_LOADER.define(className.replace('/', '.'), writer.toByteArray()).getConstructor(EntityRendererManager.class).newInstance(Minecraft.getInstance().getRenderManager());
}
private static Constructor<?> findConstructor(final Class<?> c, final Class... parameterTypes) {
try {
return c.getConstructor((Class<?>[])parameterTypes);
} catch (Exception e) {
return null;
}
}
private static Method findPreRenderCallback(final Class<? extends Entity> entityClass, Class<?> renderClass) {
while (LivingRenderer.class.isAssignableFrom(renderClass)) {
Class<?> superEntityClass = entityClass;
while (LivingEntity.class.isAssignableFrom(superEntityClass)) {
try {
return renderClass.getDeclaredMethod(PRE_RENDER_CALL_BACK.getName(), superEntityClass, Float.TYPE);
} catch (Exception ex) {
superEntityClass = superEntityClass.getSuperclass();
}
}
renderClass = renderClass.getSuperclass();
}
return null;
}
private static Field getShadowSize() throws RuntimeException {
Exception failed = null;
for (String fieldName : new HashSet<>(Arrays.asList("shadowSize", "field_76989_e"))) try {
Field field = EntityRenderer.class.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (Exception e) {
failed = e;
}
throw new RuntimeException(failed);
}
private static Method getPreRenderCallback() throws RuntimeException {
Exception failed = null;
for (String fieldName : new HashSet<>(Arrays.asList("func_77041_b", "preRenderCallback"))) try {
Method method = LivingRenderer.class.getDeclaredMethod(fieldName, LivingEntity.class, Float.TYPE);
method.setAccessible(true);
return method;
} catch (Exception e) {
failed = e;
}
throw new RuntimeException(failed);
}
}