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");
}
}