Skip to content

Commit 1208767

Browse files
committed
Reintroduce memory change counting optimization
Previously removed during update to 26.1
1 parent 9b5f3ef commit 1208767

10 files changed

Lines changed: 217 additions & 9 deletions

File tree

common/src/main/java/net/caffeinemc/mods/lithium/common/ai/MemoryModificationCounter.java

Lines changed: 0 additions & 6 deletions
This file was deleted.

common/src/main/java/net/caffeinemc/mods/lithium/common/ai/brain/BrainExtended.java renamed to common/src/main/java/net/caffeinemc/mods/lithium/common/ai/brain/memories/BrainExtended.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package net.caffeinemc.mods.lithium.common.ai.brain;
1+
package net.caffeinemc.mods.lithium.common.ai.brain.memories;
22

33
public interface BrainExtended {
44
void lithium$pretendAllMemoryTypesRegistered();
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package net.caffeinemc.mods.lithium.common.ai.brain.memories;
2+
3+
public interface MemoryModificationCounter {
4+
5+
long lithium$getMemoryValueModCount();
6+
7+
void lithium$onMemoryModified();
8+
}

common/src/main/java/net/caffeinemc/mods/lithium/common/client/SharedFields.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package net.caffeinemc.mods.lithium.common.client;
22

3-
import net.caffeinemc.mods.lithium.common.ai.brain.BrainExtended;
3+
import net.caffeinemc.mods.lithium.common.ai.brain.memories.BrainExtended;
44
import net.minecraft.world.entity.ai.Brain;
55
import org.spongepowered.asm.mixin.Unique;
66

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package net.caffeinemc.mods.lithium.mixin.ai.task.memory_changes;
2+
3+
import it.unimi.dsi.fastutil.objects.ObjectIterator;
4+
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
5+
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
6+
import net.caffeinemc.mods.lithium.common.ai.brain.memories.MemoryModificationCounter;
7+
import net.minecraft.world.entity.LivingEntity;
8+
import net.minecraft.world.entity.ai.Brain;
9+
import net.minecraft.world.entity.ai.behavior.Behavior;
10+
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
11+
import net.minecraft.world.entity.ai.memory.MemoryStatus;
12+
import org.spongepowered.asm.mixin.*;
13+
import org.spongepowered.asm.mixin.injection.At;
14+
import org.spongepowered.asm.mixin.injection.Inject;
15+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
16+
17+
import java.util.Map;
18+
19+
@Mixin(Behavior.class)
20+
public class BehaviorMixin<E extends LivingEntity> {
21+
@Mutable
22+
@Shadow
23+
@Final
24+
protected Map<MemoryModuleType<?>, MemoryStatus> entryCondition;
25+
26+
@Unique
27+
private long cachedMemoryModCount = -1;
28+
@Unique
29+
private boolean cachedHasRequiredMemoryState;
30+
31+
@Inject(method = "<init>(Ljava/util/Map;II)V", at = @At("RETURN"))
32+
private void init(Map<MemoryModuleType<?>, MemoryStatus> map, int int_1, int int_2, CallbackInfo ci) {
33+
this.entryCondition = new Reference2ObjectOpenHashMap<>(map);
34+
}
35+
36+
/**
37+
* @reason Use cached required memory state test result if memory state is unchanged
38+
* @author 2No2Name
39+
*/
40+
@Overwrite
41+
public boolean hasRequiredMemories(E entity) {
42+
Brain<?> brain = entity.getBrain();
43+
long modCount = ((MemoryModificationCounter) brain).lithium$getMemoryValueModCount();
44+
if (this.cachedMemoryModCount == modCount) {
45+
return this.cachedHasRequiredMemoryState;
46+
}
47+
this.cachedMemoryModCount = modCount;
48+
49+
ObjectIterator<Reference2ObjectMap.Entry<MemoryModuleType<?>, MemoryStatus>> fastIterator = ((Reference2ObjectOpenHashMap<MemoryModuleType<?>, MemoryStatus>) this.entryCondition).reference2ObjectEntrySet().fastIterator();
50+
while (fastIterator.hasNext()) {
51+
Reference2ObjectMap.Entry<MemoryModuleType<?>, MemoryStatus> entry = fastIterator.next();
52+
if (!brain.checkMemory(entry.getKey(), entry.getValue())) {
53+
return this.cachedHasRequiredMemoryState = false;
54+
}
55+
}
56+
57+
return this.cachedHasRequiredMemoryState = true;
58+
}
59+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package net.caffeinemc.mods.lithium.mixin.ai.task.memory_changes;
2+
3+
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
4+
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
5+
import com.llamalad7.mixinextras.sugar.Local;
6+
import net.caffeinemc.mods.lithium.common.ai.brain.memories.MemoryModificationCounter;
7+
import net.minecraft.world.entity.ai.Brain;
8+
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
9+
import net.minecraft.world.entity.ai.memory.MemorySlot;
10+
import org.spongepowered.asm.mixin.Mixin;
11+
import org.spongepowered.asm.mixin.Unique;
12+
import org.spongepowered.asm.mixin.injection.At;
13+
import org.spongepowered.asm.mixin.injection.Inject;
14+
import org.spongepowered.asm.mixin.injection.ModifyArg;
15+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
16+
17+
import java.util.function.Consumer;
18+
19+
@Mixin(Brain.class)
20+
public class BrainMixin implements MemoryModificationCounter {
21+
22+
//Tracking changes to the memory value, possibly including false positives (not calling equals()).
23+
// This does not track changes to the time to live
24+
@Unique
25+
private long memoryModCount = 1;
26+
27+
@Inject(
28+
method = { "registerMemory", "clearMemories", }, at = @At("RETURN")
29+
)
30+
private void increaseMemoryModCount(CallbackInfo ci) {
31+
this.lithium$onMemoryModified();
32+
}
33+
34+
@Inject(
35+
method = "eraseMemory", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/ai/memory/MemorySlot;clear()V")
36+
)
37+
private void increaseMemoryModCount(MemoryModuleType<?> type, CallbackInfo ci, @Local(name = "slot") MemorySlot<?> slot) {
38+
if (slot.hasValue()) {
39+
this.lithium$onMemoryModified();
40+
}
41+
}
42+
43+
@ModifyArg(method = "forgetOutdatedMemories", at = @At(value = "INVOKE", target = "Ljava/util/Collection;forEach(Ljava/util/function/Consumer;)V"))
44+
private Consumer<MemorySlot<?>> tickExpiringSlotsTrackingChanges(Consumer<MemorySlot<?>> memorySlotTicker) {
45+
return slot -> {
46+
boolean b = slot.canExpire(); // canExpire implies hasValue
47+
if (b) {
48+
slot.tick();
49+
if (!slot.hasValue()) {
50+
// Expired memory was deleted during tick
51+
this.lithium$onMemoryModified();
52+
}
53+
}
54+
};
55+
}
56+
57+
@WrapOperation(
58+
method = {
59+
"setMemoryInternal(Lnet/minecraft/world/entity/ai/memory/MemoryModuleType;Ljava/lang/Object;J)V",
60+
"setMemoryInternal(Lnet/minecraft/world/entity/ai/memory/MemoryModuleType;Ljava/lang/Object;)V"
61+
},
62+
at = @At(
63+
value = "INVOKE",
64+
target = "Lnet/minecraft/world/entity/ai/memory/MemorySlot;clear()V"
65+
)
66+
)
67+
private void clearTrackingChanges(MemorySlot<?> instance, Operation<Void> original) {
68+
if (instance.hasValue()) {
69+
this.lithium$onMemoryModified();
70+
}
71+
original.call(instance);
72+
}
73+
74+
@WrapOperation(
75+
method = "setMemoryInternal(Lnet/minecraft/world/entity/ai/memory/MemoryModuleType;Ljava/lang/Object;J)V",
76+
77+
at = @At(
78+
value = "INVOKE",
79+
target = "Lnet/minecraft/world/entity/ai/memory/MemorySlot;set(Ljava/lang/Object;J)V"
80+
)
81+
)
82+
private <T> void setTrackingChanges(MemorySlot<?> instance, T value, long timeToLive, Operation<Void> original) {
83+
if (instance.hasValue() == (value == null)) {
84+
this.lithium$onMemoryModified();
85+
}
86+
original.call(instance, value, timeToLive);
87+
}
88+
89+
@WrapOperation(
90+
method = "setMemoryInternal(Lnet/minecraft/world/entity/ai/memory/MemoryModuleType;Ljava/lang/Object;)V",
91+
at = @At(
92+
value = "INVOKE",
93+
target = "Lnet/minecraft/world/entity/ai/memory/MemorySlot;set(Ljava/lang/Object;)V"
94+
)
95+
)
96+
private <T> void setTrackingChanges(MemorySlot<?> instance, T value, Operation<Void> original) {
97+
if (instance.hasValue() == (value == null)) {
98+
this.lithium$onMemoryModified();
99+
}
100+
original.call(instance, value);
101+
}
102+
103+
@Override
104+
public long lithium$getMemoryValueModCount() {
105+
return this.memoryModCount;
106+
}
107+
108+
@Override
109+
public void lithium$onMemoryModified() {
110+
this.memoryModCount++;
111+
}
112+
113+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package net.caffeinemc.mods.lithium.mixin.ai.task.memory_changes;
2+
3+
import net.minecraft.world.entity.ai.memory.MemorySlot;
4+
import org.spongepowered.asm.mixin.Mixin;
5+
import org.spongepowered.asm.mixin.Shadow;
6+
import org.spongepowered.asm.mixin.injection.At;
7+
import org.spongepowered.asm.mixin.injection.Inject;
8+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
9+
10+
@Mixin(MemorySlot.class)
11+
public abstract class MemorySlotMixin {
12+
13+
@Shadow
14+
public abstract void clear();
15+
16+
@Shadow
17+
private long timeToLive;
18+
19+
@Inject(
20+
method = "set(Ljava/lang/Object;J)V", at = @At("RETURN")
21+
)
22+
private <T> void ensureConsistency(T value, long timeToLive, CallbackInfo ci) {
23+
if (value == null && timeToLive != Long.MAX_VALUE) {
24+
this.timeToLive = Long.MAX_VALUE;
25+
}
26+
}
27+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@MixinConfigOption(description = "Keep track of AI memory changes to skip checking AI task memory prerequisites")
2+
package net.caffeinemc.mods.lithium.mixin.ai.task.memory_changes;
3+
4+
import net.caffeinemc.gradle.MixinConfigOption;

common/src/main/java/net/caffeinemc/mods/lithium/mixin/experimental/client_tick/entity/unused_brain/BrainMixin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import it.unimi.dsi.fastutil.objects.AbstractReference2ObjectFunction;
44
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
5-
import net.caffeinemc.mods.lithium.common.ai.brain.BrainExtended;
5+
import net.caffeinemc.mods.lithium.common.ai.brain.memories.BrainExtended;
66
import net.minecraft.world.entity.ai.Brain;
77
import net.minecraft.world.entity.ai.memory.ExpirableValue;
88
import net.minecraft.world.entity.ai.memory.MemoryModuleType;

common/src/main/resources/lithium.mixins.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
"ai.sensor.replace_streams.tempting.TemptingSensorMixin",
3232
"ai.sensor.secondary_poi.SecondaryPoiSensorMixin",
3333
"ai.task.launch.BrainMixin",
34+
"ai.task.memory_changes.BehaviorMixin",
35+
"ai.task.memory_changes.BrainMixin",
36+
"ai.task.memory_changes.MemorySlotMixin",
3437
"ai.task.replace_streams.GateBehaviorMixin",
3538
"ai.task.replace_streams.RunningPolicyRunOneMixin",
3639
"ai.task.replace_streams.RunningPolicyTryAllMixin",

0 commit comments

Comments
 (0)