aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0696-Rate-options-and-timings-for-sensors-and-behaviors.patch
blob: 730ffaccf0d524eda54b9f454a8db1a51e39338c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Phoenix616 <max@themoep.de>
Date: Mon, 28 Jun 2021 22:38:29 +0100
Subject: [PATCH] Rate options and timings for sensors and behaviors

This adds config options to specify the tick rate for sensors
 and behaviors of different entity types as well as timings
 for those in order to be able to have some metrics as to which
 ones might need tweaking.

diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java
index b47b7dce26805badd422c1867733ff4bfd00e9f4..b27021a42cbed3f0648a8d0903d00d03922ae221 100644
--- a/src/main/java/co/aikar/timings/MinecraftTimings.java
+++ b/src/main/java/co/aikar/timings/MinecraftTimings.java
@@ -113,6 +113,14 @@ public final class MinecraftTimings {
         return Timings.ofSafe("Minecraft", "## tickEntity - " + entityType + " - " + type, tickEntityTimer);
     }
 
+    public static Timing getBehaviorTimings(String type) {
+        return Timings.ofSafe("## Behavior - " + type);
+    }
+
+    public static Timing getSensorTimings(String type, int rate) {
+        return Timings.ofSafe("## Sensor - " + type + " (Default rate: " + rate + ")");
+    }
+
     /**
      * Get a named timer for the specified tile entity type to track type specific timings.
      * @param entity
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index a374112dc1ecb55dc921d2a2202f6eab47be0d35..9e31046c1e6f1138e75aee11647b0ff9bf45503d 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -9,8 +9,10 @@ import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
 import net.minecraft.world.entity.MobCategory;
 import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 import org.bukkit.Bukkit;
+import org.bukkit.configuration.ConfigurationSection;
 import org.bukkit.configuration.file.YamlConfiguration;
 import org.spigotmc.SpigotWorldConfig;
 
@@ -888,4 +890,57 @@ public class PaperWorldConfig {
     private void playerCrammingDamage() {
         allowPlayerCrammingDamage = getBoolean("allow-player-cramming-damage", allowPlayerCrammingDamage);
     }
+
+    private com.google.common.collect.Table<String, String, Integer> sensorTickRates;
+    private com.google.common.collect.Table<String, String, Integer> behaviorTickRates;
+    private void tickRates() {
+        config.addDefault("world-settings.default.tick-rates.sensor.villager.secondarypoisensor", 40);
+        config.addDefault("world-settings.default.tick-rates.behavior.villager.validatenearbypoi", -1); // Example
+        log("Tick rates:");
+        sensorTickRates = loadTickRates("sensor");
+        behaviorTickRates = loadTickRates("behavior");
+    }
+
+    private com.google.common.collect.Table<String, String, Integer> loadTickRates(String type) {
+        log("  " + type + ":");
+        com.google.common.collect.Table<String, String, Integer> table = com.google.common.collect.HashBasedTable.create();
+
+        ConfigurationSection typeSection = config.getConfigurationSection("world-settings." + worldName + ".tick-rates." + type);
+        if (typeSection == null) {
+            typeSection = config.getConfigurationSection("world-settings.default.tick-rates." + type);
+        }
+        if (typeSection != null) {
+            for (String entity : typeSection.getKeys(false)) {
+                ConfigurationSection entitySection = typeSection.getConfigurationSection(entity);
+                if (entitySection != null) {
+                    log("    " + entity + ":");
+                    for (String typeName : entitySection.getKeys(false)) {
+                        if (entitySection.isInt(typeName)) {
+                            int tickRate = entitySection.getInt(typeName);
+                            table.put(entity.toLowerCase(Locale.ROOT), typeName.toLowerCase(Locale.ROOT), tickRate);
+                            log("      " + typeName + ": " + tickRate);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (table.isEmpty()) {
+            log("    None configured");
+        }
+        return table;
+    }
+
+    public int getBehaviorTickRate(String typeName, String entityType, int def) {
+        return getIntOrDefault(behaviorTickRates, typeName, entityType, def);
+    }
+
+    public int getSensorTickRate(String typeName, String entityType, int def) {
+        return getIntOrDefault(sensorTickRates, typeName, entityType, def);
+    }
+
+    private int getIntOrDefault(com.google.common.collect.Table<String, String, Integer> table, String rowKey, String columnKey, int def) {
+        Integer rate = table.get(columnKey, rowKey);
+        return rate != null && rate > -1 ? rate : def;
+    }
 }
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..c24ff2ef1054523e58892c2b35080cffb6ab744a 100644
--- a/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> {
     private long endTimestamp;
     private final int minDuration;
     private final int maxDuration;
+    // Paper start - configurable behavior tick rate and timings
+    private final String configKey;
+    private final co.aikar.timings.Timing timing;
+    // Paper end
 
     public Behavior(Map<MemoryModuleType<?>, MemoryStatus> requiredMemoryState) {
         this(requiredMemoryState, 60);
@@ -27,6 +31,15 @@ public abstract class Behavior<E extends LivingEntity> {
         this.minDuration = minRunTime;
         this.maxDuration = maxRunTime;
         this.entryCondition = requiredMemoryState;
+        // Paper start - configurable behavior tick rate and timings
+        String key = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName());
+        int lastSeparator = key.lastIndexOf('.');
+        if (lastSeparator != -1) {
+            key = key.substring(lastSeparator + 1);
+        }
+        this.configKey = key.toLowerCase(java.util.Locale.ROOT);
+        this.timing = co.aikar.timings.MinecraftTimings.getBehaviorTimings(configKey);
+        // Paper end
     }
 
     public Behavior.Status getStatus() {
@@ -34,11 +47,19 @@ public abstract class Behavior<E extends LivingEntity> {
     }
 
     public final boolean tryStart(ServerLevel world, E entity, long time) {
+        // Paper start - behavior tick rate
+        int tickRate = world.paperConfig.getBehaviorTickRate(this.configKey, entity.getType().id, -1);
+        if (tickRate > -1 && time < this.endTimestamp + tickRate) {
+            return false;
+        }
+        // Paper end
         if (this.hasRequiredMemories(entity) && this.checkExtraStartConditions(world, entity)) {
             this.status = Behavior.Status.RUNNING;
             int i = this.minDuration + world.getRandom().nextInt(this.maxDuration + 1 - this.minDuration);
             this.endTimestamp = time + (long)i;
+            this.timing.startTiming(); // Paper - behavior timings
             this.start(world, entity, time);
+            this.timing.stopTiming(); // Paper - behavior timings
             return true;
         } else {
             return false;
@@ -49,11 +70,13 @@ public abstract class Behavior<E extends LivingEntity> {
     }
 
     public final void tickOrStop(ServerLevel world, E entity, long time) {
+        this.timing.startTiming(); // Paper - behavior timings
         if (!this.timedOut(time) && this.canStillUse(world, entity, time)) {
             this.tick(world, entity, time);
         } else {
             this.doStop(world, entity, time);
         }
+        this.timing.stopTiming(); // Paper - behavior timings
 
     }
 
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 c940a910a435b20297eca493c7b27fd69be54e86..f3b8e253a5bfc3f68121dbe656ae7e2ac0f0eb1c 100644
--- a/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,21 @@ 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 final int scanRate;
     private long timeToTick;
+    // Paper start - configurable sensor tick rate and timings
+    private final String configKey;
+    private final co.aikar.timings.Timing timing;
+    // Paper end
 
     public Sensor(int senseInterval) {
+        // Paper start - configurable sensor tick rate and timings
+        String key = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName());
+        int lastSeparator = key.lastIndexOf('.');
+        if (lastSeparator != -1) {
+            key = key.substring(lastSeparator + 1);
+        }
+        this.configKey = key.toLowerCase(java.util.Locale.ROOT);
+        this.timing = co.aikar.timings.MinecraftTimings.getSensorTimings(configKey, senseInterval);
+        // Paper end
         this.scanRate = senseInterval;
         this.timeToTick = (long)RANDOM.nextInt(senseInterval);
     }
@@ -31,8 +44,12 @@ public abstract class Sensor<E extends LivingEntity> {
 
     public final void tick(ServerLevel world, E entity) {
         if (--this.timeToTick <= 0L) {
-            this.timeToTick = (long)this.scanRate;
+            // Paper start - configurable sensor tick rate and timings
+            this.timeToTick = world.paperConfig.getSensorTickRate(this.configKey, entity.getType().id, this.scanRate);
+            this.timing.startTiming();
+            // Paper end
             this.doTick(world, entity);
+            this.timing.stopTiming(); // Paper - sensor timings
         }
 
     }