Skip to content

Commit

Permalink
MixinPatchSpawnerAnimals : fix a large bottleneck
Browse files Browse the repository at this point in the history
fix a large bottleneck on modpacks caused by Entity Counter by making a thread for it
  • Loading branch information
quentin452 committed Jan 19, 2024
1 parent 785031f commit 0f8ea6b
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import fr.iamacat.optimizationsandtweaks.utils.agrona.collections.Object2ObjectHashMap;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EnumCreatureType;
import net.minecraft.entity.player.EntityPlayer;
Expand All @@ -18,6 +17,8 @@
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Unique;

import static fr.iamacat.optimizationsandtweaks.utilsformods.vanilla.CreatureCountTask.*;

@Mixin(value = SpawnerAnimals.class, priority = 999)
public class MixinPatchSpawnerAnimals {

Expand All @@ -38,10 +39,6 @@ protected static ChunkPosition func_151350_a(World world, int chunkX, int chunkZ
return new ChunkPosition(x, y, z);

}

@Unique
private static Object2ObjectHashMap optimizationsAndTweaks$eligibleChunksForSpawning = new Object2ObjectHashMap();

/**
* @author
* @reason
Expand Down Expand Up @@ -70,38 +67,6 @@ public static boolean canCreatureTypeSpawnAtLocation(EnumCreatureType creatureTy
return blockMaterial.isLiquid() && blockBelowMaterial.isLiquid() && !isNormalCubeAbove;
}

@Unique
private static boolean optimizationsAndTweaks$canCreatureSpawnOnLand(EnumCreatureType creatureType, World world, int x, int y, int z, Block block, Block blockAbove) {
if (!World.doesBlockHaveSolidTopSurface(world, x, y - 1, z)) {
return false;
}
boolean isPeacefulCreature = creatureType.getPeacefulCreature();
boolean isAnimal = creatureType.getAnimal();
if ((!isPeacefulCreature || isAnimal) && optimizationsAndTweaks$shouldSpawnCreature(creatureType, world, optimizationsAndTweaks$eligibleChunksForSpawning)) {
for (int spawnAttempt = 0; spawnAttempt < creatureType.getMaxNumberOfCreature(); spawnAttempt++) {
if (block != Blocks.bedrock && !blockAbove.isNormalCube() && !blockAbove.getMaterial().isLiquid()) {
return true;
}
}
}
return false;
}

@Unique
private static boolean optimizationsAndTweaks$shouldSpawnCreature(EnumCreatureType creatureType, World world, Object2ObjectHashMap<ChunkCoordIntPair, Boolean> eligibleChunks) {
int creatureCount = 0;
for (Object entity : world.loadedEntityList) {
if (creatureType.getCreatureClass().isInstance(entity)) {
ChunkCoordIntPair chunkCoord = new ChunkCoordIntPair(MathHelper.floor_double(((Entity) entity).posX) >> 4, MathHelper.floor_double(((Entity) entity).posZ) >> 4);
Boolean isChunkEligible = eligibleChunks.get(chunkCoord);
if (isChunkEligible != null && isChunkEligible) {
creatureCount++;
}
}
}
return creatureCount <= creatureType.getMaxNumberOfCreature() * eligibleChunks.size() / 256;
}

/**
* @author iamacatfr
* @reason optimize findChunksForSpawning
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package fr.iamacat.optimizationsandtweaks.utilsformods.vanilla;

import fr.iamacat.optimizationsandtweaks.utils.agrona.collections.Object2ObjectHashMap;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EnumCreatureType;
import net.minecraft.init.Blocks;
import net.minecraft.util.MathHelper;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.World;

import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

public class CreatureCountTask implements Runnable {
public static final Object2ObjectHashMap optimizationsAndTweaks$eligibleChunksForSpawning = new Object2ObjectHashMap();
private static final Thread countThread = new Thread(new CreatureCountTask(), "CreatureCountThread");
private static boolean threadStarted = false;

private final EnumCreatureType creatureType;
private final World world;
private final Object2ObjectHashMap<ChunkCoordIntPair, Boolean> eligibleChunks;
private final AtomicInteger result;

private CreatureCountTask() {
this.creatureType = null;
this.world = null;
this.eligibleChunks = null;
this.result = null;
}

public CreatureCountTask(EnumCreatureType creatureType, World world, Object2ObjectHashMap<ChunkCoordIntPair, Boolean> eligibleChunks, AtomicInteger result) {
this.creatureType = creatureType;
this.world = world;
this.eligibleChunks = eligibleChunks;
this.result = result;
}

@Override
public void run() {
int totalCreatureCount = 0;
int maxCreatureCount = optimizationsAndTweaks$getMaxCreatureCount(creatureType, eligibleChunks);
Iterator<?> entityIterator = world.loadedEntityList.iterator();
Class<?> creatureClass = Objects.requireNonNull(creatureType.getCreatureClass());

while (entityIterator.hasNext() && totalCreatureCount < maxCreatureCount) {
Object entity = entityIterator.next();
if (creatureClass.isInstance(entity)) {
double entityPosX = ((Entity) entity).posX;
double entityPosZ = ((Entity) entity).posZ;
int chunkX = MathHelper.floor_double(entityPosX) >> 4;
int chunkZ = MathHelper.floor_double(entityPosZ) >> 4;
ChunkCoordIntPair chunkCoord = new ChunkCoordIntPair(chunkX, chunkZ);
Boolean isChunkEligible = eligibleChunks.get(chunkCoord);
if (isChunkEligible != null && isChunkEligible) {
++totalCreatureCount;
}
}
}

assert result != null;
result.set(totalCreatureCount);
}

private static int optimizationsAndTweaks$getMaxCreatureCount(EnumCreatureType creatureType, Object2ObjectHashMap<ChunkCoordIntPair, Boolean> eligibleChunks) {
return creatureType.getMaxNumberOfCreature() * eligibleChunks.size() / 256;
}

public int getTotalCreatureCount() {
assert result != null;
return result.get();
}

public static boolean optimizationsAndTweaks$shouldSpawnCreature(EnumCreatureType creatureType, World world, Object2ObjectHashMap<ChunkCoordIntPair, Boolean> eligibleChunks) {
if (!threadStarted) {
countThread.start();
threadStarted = true;
}

try {
countThread.join(500);
} catch (InterruptedException e) {
e.printStackTrace();
}

int totalCreatureCount = new CreatureCountTask(creatureType, world, eligibleChunks, new AtomicInteger()).getTotalCreatureCount();
int maxCreatureCount = optimizationsAndTweaks$getMaxCreatureCount(creatureType, eligibleChunks);
return totalCreatureCount <= maxCreatureCount;
}

public static boolean optimizationsAndTweaks$canCreatureSpawnOnLand(EnumCreatureType creatureType, World world, int x, int y, int z, Block block, Block blockAbove) {
if (!World.doesBlockHaveSolidTopSurface(world, x, y - 1, z)) {
return false;
}
boolean isPeacefulCreature = creatureType.getPeacefulCreature();
boolean isAnimal = creatureType.getAnimal();
if ((!isPeacefulCreature || isAnimal) && optimizationsAndTweaks$shouldSpawnCreature(creatureType, world, optimizationsAndTweaks$eligibleChunksForSpawning)) {
for (int spawnAttempt = 0; spawnAttempt < creatureType.getMaxNumberOfCreature(); spawnAttempt++) {
if (block != Blocks.bedrock && !blockAbove.isNormalCube() && !blockAbove.getMaterial().isLiquid()) {
return true;
}
}
}
return false;
}
}

0 comments on commit 0f8ea6b

Please sign in to comment.