Skip to content

Commit a41e165

Browse files
committed
Optimize projectile collisions when many non-hittable entities are present
1 parent b295d71 commit a41e165

File tree

6 files changed

+79
-7
lines changed

6 files changed

+79
-7
lines changed

common/src/main/java/net/caffeinemc/mods/lithium/common/world/WorldHelper.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import net.caffeinemc.mods.lithium.common.client.ClientWorldAccessor;
44
import net.caffeinemc.mods.lithium.common.entity.EntityClassGroup;
55
import net.caffeinemc.mods.lithium.common.entity.pushable.EntityPushablePredicate;
6+
import net.caffeinemc.mods.lithium.common.services.PlatformEntityAccess;
67
import net.caffeinemc.mods.lithium.common.world.chunk.ClassGroupFilterableList;
78
import net.caffeinemc.mods.lithium.mixin.util.accessors.EntitySectionAccessor;
89
import net.caffeinemc.mods.lithium.mixin.util.accessors.PersistentEntitySectionManagerAccessor;
@@ -45,7 +46,7 @@ public static List<Entity> getEntitiesForCollision(EntityGetter entityView, AABB
4546
EntitySectionStorage<Entity> cache = getEntityCacheOrNull(world);
4647
if (cache != null) {
4748
Profiler.get().incrementCounter("getEntities");
48-
return getEntitiesOfEntityGroup(cache, collidingEntity, EntityClassGroup.NoDragonClassGroup.BOAT_SHULKER_LIKE_COLLISION, box, null);
49+
return getEntitiesOfEntityGroupWithoutDragonPieces(cache, collidingEntity, EntityClassGroup.NoDragonClassGroup.BOAT_SHULKER_LIKE_COLLISION, box, null);
4950
}
5051
}
5152
//use vanilla code in case the shortcut is not applicable
@@ -59,7 +60,7 @@ public static List<Entity> getOtherEntitiesForCollision(EntityGetter entityView,
5960
EntitySectionStorage<Entity> cache = getEntityCacheOrNull(world);
6061
if (cache != null) {
6162
Profiler.get().incrementCounter("getEntities");
62-
return getEntitiesOfEntityGroup(cache, collidingEntity, EntityClassGroup.NoDragonClassGroup.BOAT_SHULKER_LIKE_COLLISION, box, entityFilter);
63+
return getEntitiesOfEntityGroupWithoutDragonPieces(cache, collidingEntity, EntityClassGroup.NoDragonClassGroup.BOAT_SHULKER_LIKE_COLLISION, box, entityFilter);
6364
}
6465
}
6566
}
@@ -73,15 +74,21 @@ public static List<Entity> getOtherEntitiesForCollision(EntityGetter entityView,
7374
public static EntitySectionStorage<Entity> getEntityCacheOrNull(Level world) {
7475
if (world instanceof ClientWorldAccessor) {
7576
//noinspection unchecked
76-
return ((TransientEntitySectionManagerAccessor<Entity>) ((ClientWorldAccessor) world).lithium$getEntityManager()).getCache();
77+
TransientEntitySectionManagerAccessor<Entity> entityManager = (TransientEntitySectionManagerAccessor<Entity>) ((ClientWorldAccessor) world).lithium$getEntityManager();
78+
if (entityManager != null) {
79+
return entityManager.getCache();
80+
}
7781
} else if (world instanceof ServerLevelAccessor) {
7882
//noinspection unchecked
79-
return ((PersistentEntitySectionManagerAccessor<Entity>) ((ServerLevelAccessor) world).getEntityManager()).getCache();
83+
PersistentEntitySectionManagerAccessor<Entity> entityManager = (PersistentEntitySectionManagerAccessor<Entity>) ((ServerLevelAccessor) world).getEntityManager();
84+
if (entityManager != null) {
85+
return entityManager.getCache();
86+
}
8087
}
8188
return null;
8289
}
8390

84-
public static List<Entity> getEntitiesOfEntityGroup(EntitySectionStorage<Entity> cache, Entity collidingEntity, EntityClassGroup.NoDragonClassGroup entityClassGroup, AABB box, Predicate<? super Entity> entityFilter) {
91+
public static ArrayList<Entity> getEntitiesOfEntityGroupWithoutDragonPieces(EntitySectionStorage<Entity> cache, Entity excludedEntity, EntityClassGroup entityClassGroup, AABB box, Predicate<? super Entity> entityFilter) {
8592
ArrayList<Entity> entities = new ArrayList<>();
8693
cache.forEachAccessibleNonEmptySection(box, section -> {
8794
//noinspection unchecked
@@ -90,8 +97,7 @@ public static List<Entity> getEntitiesOfEntityGroup(EntitySectionStorage<Entity>
9097
Collection<Entity> entitiesOfType = ((ClassGroupFilterableList<Entity>) allEntities).lithium$getAllOfGroupType(entityClassGroup);
9198
if (!entitiesOfType.isEmpty()) {
9299
for (Entity entity : entitiesOfType) {
93-
if (entity.getBoundingBox().intersects(box) && !entity.isSpectator() && entity != collidingEntity && (entityFilter == null || entityFilter.test(entity))) {
94-
//skip the dragon piece check without issues by only allowing EntityClassGroup.NoDragonClassGroup as type
100+
if (entity.getBoundingBox().intersects(box) && !entity.isSpectator() && entity != excludedEntity && (entityFilter == null || entityFilter.test(entity))) {
95101
entities.add(entity);
96102
}
97103
}
@@ -101,6 +107,14 @@ public static List<Entity> getEntitiesOfEntityGroup(EntitySectionStorage<Entity>
101107
return entities;
102108
}
103109

110+
public static List<Entity> getEntitiesOfEntityGroupPlusDragonPieces(Level level, EntitySectionStorage<Entity> cache, Entity excludedEntity, EntityClassGroup entityClassGroup, AABB box, Predicate<? super Entity> entityFilter) {
111+
ArrayList<Entity> entities = getEntitiesOfEntityGroupWithoutDragonPieces(cache, excludedEntity, entityClassGroup, box, entityFilter);
112+
if (!level.dragonParts().isEmpty()) {
113+
PlatformEntityAccess.INSTANCE.addEnderDragonParts(level, excludedEntity, box, entityFilter, entities);
114+
}
115+
return entities;
116+
}
117+
104118
public static List<Entity> getPushableEntities(Level world, EntitySectionStorage<Entity> cache, Entity except, AABB box, EntityPushablePredicate<? super Entity> entityPushablePredicate) {
105119
ArrayList<Entity> entities = new ArrayList<>();
106120
cache.forEachAccessibleNonEmptySection(box, section -> ((ClimbingMobCachingSection) section).lithium$collectPushableEntities(world, except, box, entityPushablePredicate, entities));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package net.caffeinemc.mods.lithium.mixin.experimental.entity.projectile_projectile_collisions;
2+
3+
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
4+
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
5+
import net.caffeinemc.mods.lithium.common.entity.projectile.ProjectileEntityClassGroup;
6+
import net.caffeinemc.mods.lithium.common.world.WorldHelper;
7+
import net.minecraft.world.entity.Entity;
8+
import net.minecraft.world.entity.projectile.ProjectileUtil;
9+
import net.minecraft.world.level.Level;
10+
import net.minecraft.world.level.entity.EntitySectionStorage;
11+
import net.minecraft.world.phys.AABB;
12+
import org.jetbrains.annotations.Nullable;
13+
import org.spongepowered.asm.mixin.Mixin;
14+
import org.spongepowered.asm.mixin.injection.At;
15+
16+
import java.util.List;
17+
import java.util.function.Predicate;
18+
19+
@Mixin(ProjectileUtil.class)
20+
public class ProjectileUtilMixin {
21+
22+
@WrapOperation(
23+
method = "getEntityHitResult(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;F)Lnet/minecraft/world/phys/EntityHitResult;",
24+
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;")
25+
)
26+
private static List<Entity> getEntitiesForCollision(Level level, @Nullable Entity searchingEntity, AABB box, Predicate<? super Entity> entityFilter, Operation<List<Entity>> original) {
27+
if (searchingEntity != null && ProjectileEntityClassGroup.OPTIMIZED_PROJECTILES.contains(searchingEntity)) {
28+
EntitySectionStorage<Entity> cache = WorldHelper.getEntityCacheOrNull(level);
29+
if (cache != null) {
30+
return WorldHelper.getEntitiesOfEntityGroupPlusDragonPieces(level, cache, searchingEntity, ProjectileEntityClassGroup.CAN_MAYBE_BE_HIT_BY_OPTIMIZED_PROJECTILE, box, entityFilter);
31+
}
32+
}
33+
return original.call(level, searchingEntity, box, entityFilter);
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@MixinConfigOption(
2+
description = "Optimize huge stacks of projectiles (e.g. 1000+ ender pearls in a single statis chamber) by " +
3+
"skipping projectile-projectile collision checks for projectile types that are unable to collide with " +
4+
"each other, e.g. ender pearls never collide with ender pearls.",
5+
depends = @MixinConfigDependency(dependencyPath = "mixin.chunk.entity_class_groups")
6+
)
7+
package net.caffeinemc.mods.lithium.mixin.experimental.entity.projectile_projectile_collisions;
8+
9+
import net.caffeinemc.gradle.MixinConfigDependency;
10+
import net.caffeinemc.gradle.MixinConfigOption;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
"experimental.entity.block_caching.fire_lava_touching.EntityMixin",
144144
"experimental.entity.block_caching.suffocation.EntityMixin",
145145
"experimental.entity.item_entity_merging.ItemEntityMixin",
146+
"experimental.entity.projectile_projectile_collisions.ProjectileUtilMixin",
146147
"gen.cached_generator_settings.NoiseBasedChunkGeneratorMixin",
147148
"math.fast_blockpos.BlockPosMixin",
148149
"math.fast_blockpos.DirectionMixin",

lithium-fabric-mixin-config.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,12 @@ Requirements:
406406
- `mixin.util.entity_collection_replacement=true`
407407
- `mixin.util.item_component_and_count_tracking=true`
408408

409+
### `mixin.experimental.entity.projectile_projectile_collisions`
410+
(default: `true`)
411+
Optimize huge stacks of projectiles (e.g. 1000+ ender pearls in a single statis chamber) by skipping projectile-projectile collision checks for projectile types that are unable to collide with each other, e.g. ender pearls never collide with ender pearls.
412+
Requirements:
413+
- `mixin.chunk.entity_class_groups=true`
414+
409415
### `mixin.gen`
410416
(default: `true`)
411417
Various world generation optimizations

lithium-neoforge-mixin-config.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,12 @@ Requirements:
379379
- `mixin.util.entity_collection_replacement=true`
380380
- `mixin.util.item_component_and_count_tracking=true`
381381

382+
### `mixin.experimental.entity.projectile_projectile_collisions`
383+
(default: `true`)
384+
Optimize huge stacks of projectiles (e.g. 1000+ ender pearls in a single statis chamber) by skipping projectile-projectile collision checks for projectile types that are unable to collide with each other, e.g. ender pearls never collide with ender pearls.
385+
Requirements:
386+
- `mixin.chunk.entity_class_groups=true`
387+
382388
### `mixin.gen`
383389
(default: `true`)
384390
Various world generation optimizations

0 commit comments

Comments
 (0)