diff --git a/src/main/java/net/minecraft/entity/Entity.java b/src/main/java/net/minecraft/entity/Entity.java index a279ad2..5847a70 100644 --- a/src/main/java/net/minecraft/entity/Entity.java +++ b/src/main/java/net/minecraft/entity/Entity.java @@ -10,6 +10,7 @@ import java.util.Random; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.ThreadLocalRandom; import org.ultramine.server.EntityType; import org.ultramine.server.internal.UMHooks; @@ -164,10 +165,10 @@ this.width = 0.6F; this.height = 1.8F; this.nextStepDistance = 1; - this.rand = new Random(); + this.rand = ThreadLocalRandom.current(); // Really all access to rand replaces with TLR.current() by ThreadLocalRandomTransformer this.fireResistance = 1; this.firstUpdate = true; - this.entityUniqueID = UUID.randomUUID(); + this.entityUniqueID = new UUID(ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong()); this.myEntitySize = Entity.EnumEntitySize.SIZE_2; this.worldObj = p_i1582_1_; this.setPosition(0.0D, 0.0D, 0.0D); diff --git a/src/main/java/org/ultramine/server/asm/transformers/ThreadLocalRandomTransformer.java b/src/main/java/org/ultramine/server/asm/transformers/ThreadLocalRandomTransformer.java new file mode 100644 index 0000000..a18fd97 --- /dev/null +++ b/src/main/java/org/ultramine/server/asm/transformers/ThreadLocalRandomTransformer.java @@ -0,0 +1,94 @@ +package org.ultramine.server.asm.transformers; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.ultramine.server.UltraminePlugin; +import org.ultramine.server.asm.UMTBatchTransformer.IUMClassTransformer; +import org.ultramine.server.asm.UMTBatchTransformer.TransformResult; + +import java.util.ListIterator; + +/** + * This transformer redirects field get to method invocation:
+ * from {@link net.minecraft.entity.Entity#rand} to {@link java.util.concurrent.ThreadLocalRandom#current()} + */ +public class ThreadLocalRandomTransformer implements IUMClassTransformer +{ + private static final Logger log = LogManager.getLogger(); + + private static final String ENTITY_TYPE_OBF = "sa"; + private static final String ENTITY_TYPE_DEOBF = "net/minecraft/entity/Entity"; + private static final String FIELD_NAME_OBF = "Z"; + private static final String FIELD_DESC = "Ljava/util/Random;"; + private static final String TLR_TYPE = "java/util/concurrent/ThreadLocalRandom"; + private static final String METHOD_NAME = "current"; + private static final String METHOD_DESC = "()Ljava/util/concurrent/ThreadLocalRandom;"; + + @Override + public TransformResult transform(String name, String transformedName, ClassReader classReader, ClassNode classNode) + { + boolean modified = false; + for(MethodNode m : classNode.methods) + { + for(ListIterator it = m.instructions.iterator(); it.hasNext(); ) + { + AbstractInsnNode insnNode = it.next(); + if(insnNode.getOpcode() == Opcodes.GETFIELD) + { + FieldInsnNode fi = (FieldInsnNode)insnNode; + String fieldNameObf = UltraminePlugin.isObfEnv ? "field_70146_Z" : "rand"; + if((FIELD_NAME_OBF.equals(fi.name) || fieldNameObf.equals(fi.name)) && FIELD_DESC.equals(fi.desc) && isEntityType(classNode, fi.owner)) + { + log.trace("Method {}.{}{}: Replacing GETFIELD {}.random with INVOKESTATIC ThreadLocalRandom.current", name, m.name, m.desc, fi.owner); + it.remove(); + // !it.hasPrevious() is impossible here + if(it.previous().getOpcode() == Opcodes.ALOAD) + { + it.remove(); + } + else + { + log.trace("\t\t-- Using POP insn"); + it.next(); + it.add(new InsnNode(Opcodes.POP)); + } + it.add(new MethodInsnNode(Opcodes.INVOKESTATIC, TLR_TYPE, METHOD_NAME, METHOD_DESC, false)); + modified = true; + } + } + } + } + + return modified ? TransformResult.MODIFIED : TransformResult.NOT_MODIFIED; + } + + private boolean isEntityType(ClassNode classNode, String fieldOwner) + { + if(ENTITY_TYPE_OBF.equals(fieldOwner) || ENTITY_TYPE_DEOBF.equals(fieldOwner)) + return true; + if(!classNode.name.equals(fieldOwner)) + return false; + + try + { + Class parent = this.getClass().getClassLoader().loadClass(classNode.superName.replace('/', '.')); + if(parent.getSuperclass() == null) + return false; + while(parent.getSuperclass() != Object.class) + parent = parent.getSuperclass(); + return parent.getName().equals("net.minecraft.entity.Entity"); + } + catch(ClassNotFoundException e) + { + return false; + } + } +} diff --git a/src/main/java/org/ultramine/server/asm/transformers/UMTransformerCollection.java b/src/main/java/org/ultramine/server/asm/transformers/UMTransformerCollection.java index abe143e..df459ec 100644 --- a/src/main/java/org/ultramine/server/asm/transformers/UMTransformerCollection.java +++ b/src/main/java/org/ultramine/server/asm/transformers/UMTransformerCollection.java @@ -8,6 +8,7 @@ { registerGlobalTransformer(new PrintStackTraceTransformer()); registerGlobalTransformer(new TrigMathTransformer()); + registerGlobalTransformer(new ThreadLocalRandomTransformer()); registerSpecialTransformer(new BlockLeavesBaseFixer(), "net.minecraft.block.BlockLeavesBase"); } }