Skip to content

Commit 1aa44e9

Browse files
committed
Fix experimental client brain crash due to type error
#732
1 parent 5d8b592 commit 1aa44e9

6 files changed

Lines changed: 76 additions & 9 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
cache-read-only: true
4242

4343
- name: Validate Gradle Wrapper Integrity
44-
uses: gradle/actions/wrapper-validation@v4
44+
uses: gradle/actions/wrapper-validation@v5
4545

4646
- name: Grant execute permission for gradlew
4747
run: chmod +x gradlew

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22

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

77
import java.util.concurrent.atomic.AtomicInteger;
88

99
public class SharedFields {
1010
public static final AtomicInteger MAXIMUM_BIOME_PARTICLE_CHANCE = new AtomicInteger(Float.floatToIntBits(0.0F)); //Using atomic integer as replacement for atomic float
11-
@Unique
11+
public static final MemorySlot<?> DUMMY_SLOT;
1212
public static final Brain<?> DUMMY_BRAIN;
1313

1414
static {
15+
DUMMY_SLOT = MemorySlot.create();
16+
1517
var brain = new Brain<>();
1618
((BrainExtended) brain).lithium$pretendAllMemoryTypesRegistered();
1719
DUMMY_BRAIN = brain;

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,34 @@
33
import it.unimi.dsi.fastutil.objects.AbstractReference2ObjectFunction;
44
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
55
import net.caffeinemc.mods.lithium.common.ai.brain.memories.BrainExtended;
6+
import net.caffeinemc.mods.lithium.common.client.SharedFields;
67
import net.minecraft.world.entity.ai.Brain;
7-
import net.minecraft.world.entity.ai.memory.ExpirableValue;
88
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
9+
import net.minecraft.world.entity.ai.memory.MemorySlot;
910
import org.spongepowered.asm.mixin.Final;
1011
import org.spongepowered.asm.mixin.Mixin;
1112
import org.spongepowered.asm.mixin.Mutable;
1213
import org.spongepowered.asm.mixin.Shadow;
1314

1415
import java.util.Map;
15-
import java.util.Optional;
1616

1717
@Mixin(value = Brain.class)
1818
public class BrainMixin implements BrainExtended {
1919

2020
@Mutable
2121
@Shadow
2222
@Final
23-
private Map<MemoryModuleType<?>, Optional<? extends ExpirableValue<?>>> memories;
23+
private Map<MemoryModuleType<?>, MemorySlot<?>> memories;
2424

2525

2626
@Override
2727
public void lithium$pretendAllMemoryTypesRegistered() {
2828
if (this.memories instanceof AbstractReference2ObjectFunction<?, ?> memoryCollection) {
2929
//noinspection unchecked
30-
((AbstractReference2ObjectFunction<MemoryModuleType<?>, Optional<? extends ExpirableValue<?>>>) memoryCollection).defaultReturnValue(Optional.empty());
30+
((AbstractReference2ObjectFunction<MemoryModuleType<?>, MemorySlot<?>>) memoryCollection).defaultReturnValue(SharedFields.DUMMY_SLOT);
3131
} else {
32-
Reference2ObjectOpenHashMap<MemoryModuleType<?>, Optional<? extends ExpirableValue<?>>> memoryCollection = new Reference2ObjectOpenHashMap<>(this.memories);
33-
memoryCollection.defaultReturnValue(Optional.empty());
32+
Reference2ObjectOpenHashMap<MemoryModuleType<?>, MemorySlot<?>> memoryCollection = new Reference2ObjectOpenHashMap<>(this.memories);
33+
memoryCollection.defaultReturnValue(SharedFields.DUMMY_SLOT);
3434
this.memories = memoryCollection;
3535
}
3636
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package net.caffeinemc.mods.lithium.mixin.experimental.client_tick.entity.unused_brain;
2+
3+
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
4+
import net.minecraft.world.entity.Entity;
5+
import net.minecraft.world.entity.EntityType;
6+
import net.minecraft.world.entity.ai.Brain;
7+
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
8+
import net.minecraft.world.entity.animal.golem.CopperGolem;
9+
import net.minecraft.world.level.Level;
10+
import org.jspecify.annotations.Nullable;
11+
import org.spongepowered.asm.mixin.Mixin;
12+
import org.spongepowered.asm.mixin.injection.At;
13+
14+
@Mixin(CopperGolem.class)
15+
public abstract class CopperGolemMixin extends Entity {
16+
17+
public CopperGolemMixin(EntityType<?> type, Level level) {
18+
super(type, level);
19+
}
20+
21+
@WrapWithCondition(
22+
method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/ai/Brain;setMemory(Lnet/minecraft/world/entity/ai/memory/MemoryModuleType;Ljava/lang/Object;)V")
23+
)
24+
private <U> boolean isServerSide(Brain instance, MemoryModuleType<U> type, @Nullable U value) {
25+
return !this.level().isClientSide();
26+
}
27+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package net.caffeinemc.mods.lithium.mixin.experimental.client_tick.entity.unused_brain;
2+
3+
import net.caffeinemc.mods.lithium.common.client.SharedFields;
4+
import net.minecraft.world.entity.ai.memory.MemorySlot;
5+
import org.spongepowered.asm.mixin.Mixin;
6+
import org.spongepowered.asm.mixin.Unique;
7+
import org.spongepowered.asm.mixin.injection.At;
8+
import org.spongepowered.asm.mixin.injection.Inject;
9+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
10+
11+
@Mixin(MemorySlot.class)
12+
public class MemorySlotMixin<T> {
13+
14+
private static final String CRASH_MESSAGE = "Dummy client side brain memory slot cannot be modified! This is an optimization introduced by lithium. " +
15+
"Since minecraft clients do not execute mob AI logic, allocating complete brains is unnecessary. " +
16+
"This crash is thrown when a mod tries to write memories to client side brains anyway. If this really is " +
17+
"intended behavior, mod authors can check https://github.com/CaffeineMC/lithium/wiki/Disabling-Lithium's-Mixins-using-your-mod's-fabric.mod.json-or-neoforge.mods.toml " +
18+
"and disable the setting mixin.experimental.client_tick.entity.unused_brain while users can edit their " +
19+
"configuration file https://github.com/CaffeineMC/lithium/blob/develop/lithium-mixin-config.md to disable the same setting.";
20+
21+
@Inject(
22+
method = "set(Ljava/lang/Object;J)V", at = @At("HEAD")
23+
)
24+
private void throwIfDummyMemorySlot(T value, long timeToLive, CallbackInfo ci) {
25+
if ((Object) this == SharedFields.DUMMY_SLOT) {
26+
throwOnModifyDummyMemorySlot();
27+
}
28+
}
29+
30+
@Unique
31+
private static void throwOnModifyDummyMemorySlot() {
32+
throw new UnsupportedOperationException(
33+
CRASH_MESSAGE
34+
);
35+
}
36+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@
146146
"entity.replace_entitytype_predicates.OldMinecartBehaviorMixin",
147147
"entity.sprinting_particles.EntityMixin",
148148
"experimental.client_tick.entity.unused_brain.BrainMixin",
149+
"experimental.client_tick.entity.unused_brain.CopperGolemMixin",
150+
"experimental.client_tick.entity.unused_brain.MemorySlotMixin",
149151
"experimental.entity.block_caching.EntityMixin",
150152
"experimental.entity.block_caching.block_support.EntityMixin",
151153
"experimental.entity.block_caching.suffocation.EntityMixin",

0 commit comments

Comments
 (0)