[ci skip] Put mappings util in a separate class to the stacktrace deobfuscator (#6230)

This commit is contained in:
Jason 2021-07-19 19:22:18 -07:00 committed by GitHub
parent f25facb302
commit 44516b1d8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 129 additions and 105 deletions

View file

@ -157,60 +157,49 @@ index 0000000000000000000000000000000000000000..d019802a36dbaca4bf299a55d28381e4
+ return new StacktraceDeobfuscatingRewritePolicy(); + return new StacktraceDeobfuscatingRewritePolicy();
+ } + }
+} +}
diff --git a/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java diff --git a/src/main/java/io/papermc/paper/util/ObfHelper.java b/src/main/java/io/papermc/paper/util/ObfHelper.java
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d026a7061f index 0000000000000000000000000000000000000000..271e0a2ec549836a32565bb9100d432cd68d6046
--- /dev/null --- /dev/null
+++ b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java +++ b/src/main/java/io/papermc/paper/util/ObfHelper.java
@@ -0,0 +1,227 @@ @@ -0,0 +1,87 @@
+package io.papermc.paper.util; +package io.papermc.paper.util;
+ +
+import com.destroystokyo.paper.PaperConfig;
+import com.google.common.base.Charsets; +import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap;
+import com.mojang.datafixers.util.Pair; +import com.mojang.datafixers.util.Pair;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import java.io.BufferedReader; +import java.io.BufferedReader;
+import java.io.IOException; +import java.io.IOException;
+import java.io.InputStream; +import java.io.InputStream;
+import java.io.InputStreamReader; +import java.io.InputStreamReader;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map; +import java.util.Map;
+import net.fabricmc.mapping.tree.ClassDef; +import net.fabricmc.mapping.tree.ClassDef;
+import net.fabricmc.mapping.tree.MethodDef; +import net.fabricmc.mapping.tree.MethodDef;
+import net.fabricmc.mapping.tree.TinyMappingFactory; +import net.fabricmc.mapping.tree.TinyMappingFactory;
+import net.fabricmc.mapping.tree.TinyTree; +import net.fabricmc.mapping.tree.TinyTree;
+import org.jetbrains.annotations.NotNull; +import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.Nullable; +import org.checkerframework.checker.nullness.qual.Nullable;
+import org.objectweb.asm.ClassReader; +import org.checkerframework.framework.qual.DefaultQualifier;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+ +
+public enum StacktraceDeobfuscator { +@DefaultQualifier(NonNull.class)
+public enum ObfHelper {
+ INSTANCE; + INSTANCE;
+ +
+ private static final String MOJANG_PLUS_YARN_NAMESPACE = "mojang+yarn"; + public static final String MOJANG_PLUS_YARN_NAMESPACE = "mojang+yarn";
+ private static final String SPIGOT_NAMESPACE = "spigot"; + public static final String SPIGOT_NAMESPACE = "spigot";
+ +
+ private final @Nullable Map<String, ClassMapping> mappings; + private final @Nullable Map<String, ClassMapping> mappings;
+ private final Map<Class<?>, Map<Pair<String, String>, IntList>> lineMapCache = Collections.synchronizedMap(new LinkedHashMap<>(64, 0.75f, true) {
+ @Override
+ protected boolean removeEldestEntry(final Map.Entry<Class<?>, Map<Pair<String, String>, IntList>> eldest) {
+ return this.size() > 63;
+ }
+ });
+ +
+ StacktraceDeobfuscator() { + ObfHelper() {
+ this.mappings = loadMappingsIfPresent(); + this.mappings = loadMappingsIfPresent();
+ } + }
+ +
+ public @Nullable Map<String, ClassMapping> mappings() {
+ return this.mappings;
+ }
+
+ private static @Nullable Map<String, ClassMapping> loadMappingsIfPresent() { + private static @Nullable Map<String, ClassMapping> loadMappingsIfPresent() {
+ try (final InputStream mappingsInputStream = StacktraceDeobfuscator.class.getClassLoader().getResourceAsStream("META-INF/mappings/reobf.tiny")) { + try (final @Nullable InputStream mappingsInputStream = StacktraceDeobfuscator.class.getClassLoader().getResourceAsStream("META-INF/mappings/reobf.tiny")) {
+ if (mappingsInputStream == null) { + if (mappingsInputStream == null) {
+ return null; + return null;
+ } + }
@ -249,6 +238,55 @@ index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d0
+ } + }
+ } + }
+ +
+ public record ClassMapping(
+ String obfName,
+ String mojangName,
+ Map<Pair<String, String>, MethodMapping> methodMappings
+ ) {}
+
+ public record MethodMapping(
+ String obfName,
+ String mojangName,
+ String descriptor
+ ) {}
+}
diff --git a/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ef365dfd3caa0a726983fc57125f4d9a86959fc
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
@@ -0,0 +1,156 @@
+package io.papermc.paper.util;
+
+import com.destroystokyo.paper.PaperConfig;
+import com.mojang.datafixers.util.Pair;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+@DefaultQualifier(NonNull.class)
+public enum StacktraceDeobfuscator {
+ INSTANCE;
+
+ private final Map<Class<?>, Map<Pair<String, String>, IntList>> lineMapCache = Collections.synchronizedMap(new LinkedHashMap<>(128, 0.75f, true) {
+ @Override
+ protected boolean removeEldestEntry(final Map.Entry<Class<?>, Map<Pair<String, String>, IntList>> eldest) {
+ return this.size() > 127;
+ }
+ });
+
+ public void deobfuscateThrowable(final Throwable throwable) { + public void deobfuscateThrowable(final Throwable throwable) {
+ if (!PaperConfig.deobfuscateStacktraces) { + if (!PaperConfig.deobfuscateStacktraces) {
+ return; + return;
@ -269,7 +307,8 @@ index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d0
+ return traceElements; + return traceElements;
+ } + }
+ +
+ if (this.mappings == null || traceElements.length == 0) { + final @Nullable Map<String, ObfHelper.ClassMapping> mappings = ObfHelper.INSTANCE.mappings();
+ if (mappings == null || traceElements.length == 0) {
+ return traceElements; + return traceElements;
+ } + }
+ final StackTraceElement[] result = new StackTraceElement[traceElements.length]; + final StackTraceElement[] result = new StackTraceElement[traceElements.length];
@ -279,21 +318,22 @@ index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d0
+ final String className = element.getClassName(); + final String className = element.getClassName();
+ final String methodName = element.getMethodName(); + final String methodName = element.getMethodName();
+ +
+ final ClassMapping classMapping = this.mappings.get(className); + final ObfHelper.ClassMapping classMapping = mappings.get(className);
+ if (classMapping == null) { + if (classMapping == null) {
+ result[i] = element; + result[i] = element;
+ continue; + continue;
+ } + }
+ +
+ final Pair<String, String> nameDescriptorPair; + final Class<?> clazz;
+ try { + try {
+ final Class<?> clazz = Class.forName(className); + clazz = Class.forName(className);
+ nameDescriptorPair = this.determineMethodForLine(clazz, element.getLineNumber()); + } catch (final ClassNotFoundException ex) {
+ } catch (final ReflectiveOperationException ex) {
+ throw new RuntimeException(ex); + throw new RuntimeException(ex);
+ } + }
+ + final @Nullable Pair<String, String> nameDescriptorPair = this.determineMethodForLine(clazz, element.getLineNumber());
+ final MethodMapping methodMapping = classMapping.methodMappings().get(nameDescriptorPair); + final ObfHelper.@Nullable MethodMapping methodMapping = nameDescriptorPair == null
+ ? null
+ : classMapping.methodMappings().get(nameDescriptorPair);
+ +
+ result[i] = new StackTraceElement( + result[i] = new StackTraceElement(
+ element.getClassLoaderName(), + element.getClassLoaderName(),
@ -301,14 +341,38 @@ index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d0
+ element.getModuleVersion(), + element.getModuleVersion(),
+ classMapping.mojangName(), + classMapping.mojangName(),
+ methodMapping != null ? methodMapping.mojangName() : methodName, + methodMapping != null ? methodMapping.mojangName() : methodName,
+ this.mappedFileName(classMapping.mojangName()), + sourceFileName(classMapping.mojangName()),
+ element.getLineNumber() + element.getLineNumber()
+ ); + );
+ } + }
+ return result; + return result;
+ } + }
+ +
+ private static @NotNull Map<Pair<String, String>, IntList> buildLineMap(final @NotNull Class<?> key) { + private @Nullable Pair<String, String> determineMethodForLine(final Class<?> clazz, final int lineNumber) {
+ final Map<Pair<String, String>, IntList> lineMap = this.lineMapCache.computeIfAbsent(clazz, StacktraceDeobfuscator::buildLineMap);
+ for (final var entry : lineMap.entrySet()) {
+ final Pair<String, String> pair = entry.getKey();
+ final IntList lines = entry.getValue();
+ for (int i = 0, linesSize = lines.size(); i < linesSize; i++) {
+ final int num = lines.getInt(i);
+ if (num == lineNumber) {
+ return pair;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static String sourceFileName(final String fullClassName) {
+ final int dot = fullClassName.lastIndexOf('.');
+ final String className = dot == -1
+ ? fullClassName
+ : fullClassName.substring(dot + 1);
+ final String rootClassName = className.split("\\$")[0];
+ return rootClassName + ".java";
+ }
+
+ private static Map<Pair<String, String>, IntList> buildLineMap(final Class<?> key) {
+ final Map<Pair<String, String>, IntList> lineMap = new HashMap<>(); + final Map<Pair<String, String>, IntList> lineMap = new HashMap<>();
+ final class LineCollectingMethodVisitor extends MethodVisitor { + final class LineCollectingMethodVisitor extends MethodVisitor {
+ private final IntList lines = new IntArrayList(); + private final IntList lines = new IntArrayList();
@ -347,48 +411,6 @@ index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d0
+ } + }
+ return lineMap; + return lineMap;
+ } + }
+
+ private @Nullable Pair<String, String> determineMethodForLine(final @NotNull Class<?> clazz, final int lineNumber) {
+ final Map<Pair<String, String>, IntList> lineMap = this.lineMapCache.computeIfAbsent(clazz, StacktraceDeobfuscator::buildLineMap);
+ for (final var entry : lineMap.entrySet()) {
+ final Pair<String, String> pair = entry.getKey();
+ final IntList lines = entry.getValue();
+ for (int i = 0, linesSize = lines.size(); i < linesSize; i++) {
+ final int num = lines.getInt(i);
+ if (num == lineNumber) {
+ return pair;
+ }
+ }
+ }
+ return null;
+ }
+
+ private @NotNull String mappedFileName(final @NotNull String fullClassName) {
+ final int dot = fullClassName.lastIndexOf('.');
+ final String className = dot == -1
+ ? fullClassName
+ : fullClassName.substring(dot + 1);
+ final String rootClassName = className.split("\\$")[0];
+ return rootClassName + ".java";
+ }
+
+ public record ClassMapping(
+ String obfName,
+ String mojangName,
+ Map<Pair<String, String>, MethodMapping> methodMappings
+ ) {
+ }
+
+ public record MethodMapping(
+ String obfName,
+ String mojangName,
+ String descriptor
+ ) {
+ }
+
+ public @Nullable Map<String, ClassMapping> mappings() {
+ return mappings;
+ }
+} +}
diff --git a/src/main/java/io/papermc/paper/util/TraceUtil.java b/src/main/java/io/papermc/paper/util/TraceUtil.java diff --git a/src/main/java/io/papermc/paper/util/TraceUtil.java b/src/main/java/io/papermc/paper/util/TraceUtil.java
index 2d5494d2813b773e60ddba6790b750a9a08f21f8..7695bf44503f161523ea612ef8a884ae574a2e21 100644 index 2d5494d2813b773e60ddba6790b750a9a08f21f8..7695bf44503f161523ea612ef8a884ae574a2e21 100644
@ -437,14 +459,14 @@ index 3941e14d1c3e6e688e28904948039c8b2200de5f..a4fda4a3bae9ce600e778b44cd3ef432
} }
} }
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index db0110120f88bc06954fa8c44b2a45f0c8229574..6d7eef79de7a899ccdbc3194d925bb4caa0a4b03 100644 index db0110120f88bc06954fa8c44b2a45f0c8229574..7b6c547e71230fbb3733f99a4597b3f5b51547b8 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -221,6 +221,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface @@ -221,6 +221,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
com.destroystokyo.paper.PaperConfig.registerCommands(); com.destroystokyo.paper.PaperConfig.registerCommands();
com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now
io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider
+ io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.getClass(); // load mappings for stacktrace deobf + io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // load mappings for stacktrace deobf and etc.
// Paper end // Paper end
this.setPvpAllowed(dedicatedserverproperties.pvp); this.setPvpAllowed(dedicatedserverproperties.pvp);

View file

@ -111,7 +111,7 @@ index 171321d4f1b73a25266175dfb5529dfc5cb8a5ca..f65e3b51e876f7a3d30710eed56fdca4
} }
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
index b1212e162ba938b3abe0df747a633ba9cbbe57c8..40a575cfa7670f24578f603a3e445f763e2279b5 100644 index b1212e162ba938b3abe0df747a633ba9cbbe57c8..b1928807851a4fca53e117573b25c62a6deeb8e1 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java --- a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
@@ -14,6 +14,10 @@ public abstract class Behavior<E extends LivingEntity> { @@ -14,6 +14,10 @@ public abstract class Behavior<E extends LivingEntity> {
@ -125,21 +125,22 @@ index b1212e162ba938b3abe0df747a633ba9cbbe57c8..40a575cfa7670f24578f603a3e445f76
public Behavior(Map<MemoryModuleType<?>, MemoryStatus> requiredMemoryState) { public Behavior(Map<MemoryModuleType<?>, MemoryStatus> requiredMemoryState) {
this(requiredMemoryState, 60); this(requiredMemoryState, 60);
@@ -27,6 +31,21 @@ public abstract class Behavior<E extends LivingEntity> { @@ -27,6 +31,22 @@ public abstract class Behavior<E extends LivingEntity> {
this.minDuration = minRunTime; this.minDuration = minRunTime;
this.maxDuration = maxRunTime; this.maxDuration = maxRunTime;
this.entryCondition = requiredMemoryState; this.entryCondition = requiredMemoryState;
+ // Paper start - configurable behavior tick rate and timings + // Paper start - configurable behavior tick rate and timings
+ Map<String, io.papermc.paper.util.StacktraceDeobfuscator.ClassMapping> mappings = io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.mappings(); + String key = this.getClass().getSimpleName();
+ String key; + final var mappings = io.papermc.paper.util.ObfHelper.INSTANCE.mappings();
+ if (mappings != null) { + if (mappings != null) {
+ key = mappings.get(getClass().getName()).mojangName(); + final var classMapping = mappings.get(this.getClass().getName());
+ int lastSeparator = key.lastIndexOf('.'); + if (classMapping != null) {
+ if (lastSeparator != -1) { + key = classMapping.mojangName();
+ key = key.substring(lastSeparator + 1); + int lastSeparator = key.lastIndexOf('.');
+ if (lastSeparator != -1) {
+ key = key.substring(lastSeparator + 1);
+ }
+ } + }
+ } else {
+ key = getClass().getSimpleName();
+ } + }
+ this.configKey = key.toLowerCase(java.util.Locale.ROOT); + this.configKey = key.toLowerCase(java.util.Locale.ROOT);
+ this.timing = co.aikar.timings.MinecraftTimings.getBehaviorTimings(configKey); + this.timing = co.aikar.timings.MinecraftTimings.getBehaviorTimings(configKey);
@ -147,7 +148,7 @@ index b1212e162ba938b3abe0df747a633ba9cbbe57c8..40a575cfa7670f24578f603a3e445f76
} }
public Behavior.Status getStatus() { public Behavior.Status getStatus() {
@@ -34,11 +53,19 @@ public abstract class Behavior<E extends LivingEntity> { @@ -34,11 +54,19 @@ public abstract class Behavior<E extends LivingEntity> {
} }
public final boolean tryStart(ServerLevel world, E entity, long time) { public final boolean tryStart(ServerLevel world, E entity, long time) {
@ -167,7 +168,7 @@ index b1212e162ba938b3abe0df747a633ba9cbbe57c8..40a575cfa7670f24578f603a3e445f76
return true; return true;
} else { } else {
return false; return false;
@@ -49,11 +76,13 @@ public abstract class Behavior<E extends LivingEntity> { @@ -49,11 +77,13 @@ public abstract class Behavior<E extends LivingEntity> {
} }
public final void tickOrStop(ServerLevel world, E entity, long time) { public final void tickOrStop(ServerLevel world, E entity, long time) {
@ -182,10 +183,10 @@ index b1212e162ba938b3abe0df747a633ba9cbbe57c8..40a575cfa7670f24578f603a3e445f76
} }
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java
index f94aa5147c52d2e36d6018f51b85e9dac7a6208a..188dc5bf48d5c1f624ac117db5e79101a441370b 100644 index f94aa5147c52d2e36d6018f51b85e9dac7a6208a..ef0677e8a09800a5eeb30ae6575fbd5acf181a7c 100644
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java --- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java +++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java
@@ -19,8 +19,27 @@ public abstract class Sensor<E extends LivingEntity> { @@ -19,8 +19,28 @@ public abstract class Sensor<E extends LivingEntity> {
private static final TargetingConditions ATTACK_TARGET_CONDITIONS_IGNORE_INVISIBILITY_AND_LINE_OF_SIGHT = TargetingConditions.forCombat().range(16.0D).ignoreLineOfSight().ignoreInvisibilityTesting(); private static final TargetingConditions ATTACK_TARGET_CONDITIONS_IGNORE_INVISIBILITY_AND_LINE_OF_SIGHT = TargetingConditions.forCombat().range(16.0D).ignoreLineOfSight().ignoreInvisibilityTesting();
private final int scanRate; private final int scanRate;
private long timeToTick; private long timeToTick;
@ -196,16 +197,17 @@ index f94aa5147c52d2e36d6018f51b85e9dac7a6208a..188dc5bf48d5c1f624ac117db5e79101
public Sensor(int senseInterval) { public Sensor(int senseInterval) {
+ // Paper start - configurable sensor tick rate and timings + // Paper start - configurable sensor tick rate and timings
+ java.util.Map<String, io.papermc.paper.util.StacktraceDeobfuscator.ClassMapping> mappings = io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.mappings(); + String key = this.getClass().getSimpleName();
+ String key; + final var mappings = io.papermc.paper.util.ObfHelper.INSTANCE.mappings();
+ if (mappings != null) { + if (mappings != null) {
+ key = mappings.get(getClass().getName()).mojangName(); + final var classMapping = mappings.get(this.getClass().getName());
+ int lastSeparator = key.lastIndexOf('.'); + if (classMapping != null) {
+ if (lastSeparator != -1) { + key = classMapping.mojangName();
+ key = key.substring(lastSeparator + 1); + int lastSeparator = key.lastIndexOf('.');
+ if (lastSeparator != -1) {
+ key = key.substring(lastSeparator + 1);
+ }
+ } + }
+ } else {
+ key = getClass().getSimpleName();
+ } + }
+ this.configKey = key.toLowerCase(java.util.Locale.ROOT); + this.configKey = key.toLowerCase(java.util.Locale.ROOT);
+ this.timing = co.aikar.timings.MinecraftTimings.getSensorTimings(configKey); + this.timing = co.aikar.timings.MinecraftTimings.getSensorTimings(configKey);
@ -213,7 +215,7 @@ index f94aa5147c52d2e36d6018f51b85e9dac7a6208a..188dc5bf48d5c1f624ac117db5e79101
this.scanRate = senseInterval; this.scanRate = senseInterval;
this.timeToTick = (long)RANDOM.nextInt(senseInterval); this.timeToTick = (long)RANDOM.nextInt(senseInterval);
} }
@@ -31,8 +50,12 @@ public abstract class Sensor<E extends LivingEntity> { @@ -31,8 +51,12 @@ public abstract class Sensor<E extends LivingEntity> {
public final void tick(ServerLevel world, E entity) { public final void tick(ServerLevel world, E entity) {
if (--this.timeToTick <= 0L) { if (--this.timeToTick <= 0L) {