Kibe Utilities

Kibe Utilities

8M Downloads

Cursed dirt changes Big Torches behavior which leads to random mobs spawning

Tsudico opened this issue ยท 4 comments

commented

I tried to set up a mob farm on a server using Cursed Dirt so that I could collect mob resources. I had previously placed a Big Torch and it has a full set of torches within it. There are also additional Big Torches in farther chunks to cover a larger area. Prior to placing the cursed seed on the dirt to make Cursed Dirt, no mobs would spawn anywhere within the chunks covered by the Big Torches. After letting the Cursed Dirt spread for a little while along with mobs spawning within the Cursed Dirt mob farm, areas that should be covered by the Big Torches are now starting to spawn mobs.

I originally thought it might be cursed Endermen picking up the Cursed Dirt and placing it elsewhere, but no dirt is missing from the mob farm and I can't find any Cursed Dirt in areas where mobs are spawning. The mobs that are spawning do not look like cursed versions but regular mobs that would spawn if the Big Torches no longer were covering an area.

Here is how I think it's happening:

  1. Cursed Dirt has a randomTick method and within it calls getSpawnableMonster to try and spawn a mob. See CursedCursedDirt.kt#L94
  2. Within getSpawnableMonster it calls the singleton for BigTorchBlockEntity and sets the exception to true. See CursedDirt.kt#L142
  3. Looking at BigTorchBlockEntity, the exception modifies the behavior of isChunkSuppressed so that it returns false. The method isChunkSuppressed doesn't seem to take into consideration which chunk should be the exception nor whether the chunk that is calling isChunkSuppressed is the chunk that set the exception. When combined with thread concurrency, it is possible that between the time that Cursed Dirt sets the exception and checks if the chunk is suppressed that another call to isChunkSuppressed has taken the exception allowing a different chunk within any Big Torch's boundary to spawn a mob. This matches behavior that I am seeing where mobs are spawning in random chunks that are "protected" by a Big Torch. See BigTorchBlockEntity.kt#L147
  4. More Cursed Dirt blocks, or more chunks with the blocks in them, will lead to larger probabilities of the randomTick method call. This means that as time goes on and the Cursed Dirt spreads, there is a greater chance for a possible concurrency issue and more likely that any other chunk within a Big Torch's influence will allow mobs to spawn. If the chunk with Cursed Dirt is chunk loaded (whether through a nearby player or chuck loader from mods) then it increases the length of time that this can go on leading to the following behavior:
    • Mobs can spawn according to normal minecraft spawning within Big Torch covered chunks
    • If mobs within a Cursed Dirt chunk are moved to a different chunk like often is done within a larger mob farm, they will never exceed the Cursed Dirt mob capacity limit and so the chunks the mobs are removed from will continue to try to create new mobs and new BigTorchBlockEntity exceptions
    • Because of the previous two points, mobs will slowly increase in number within chunk loaded Big Torch covered chunks that do not have Cursed Dirt in them

I would imagine solving the problem is not simple. While code could be added to verify which chunk the exception is for, it would not work for people who build mob farms either above or below their main bases while depending on the Big Torch to prevent mobs from spawning in those bases.

A better solution might require changing the suppression check to be more thorough in HostileEntityMixin.java#L20 to get rid of the exceptions needed due to the Cursed Dirt. I am not an expert at Kotlin and rusty with fabric mod development so am unsure of how one might specifically be able to compare the block below to CursedDirt, but I would imagine it would be something like:

        if(BigTorchBlockEntity.Companion.isChunkSuppressed(world.toServerWorld().getRegistryKey(), new ChunkPos(pos))) {
+            // Include checking the block beneath the current pos to see if it is a CursedDirt block and return true if it is
+            if(world.toServerWorld().getBlockState(pos.down()).getBlock() is CursedDirt) return info.setReturnValue(true);
+            // otherwise return false like before
            info.setReturnValue(false);
        }

This would allow normal, non-cursed mobs to spawn on Cursed Dirt but that might be an acceptable alternative.

Finally, one could possibly change how the spawn check is done within getSpawnableMonster so that it doesn't actually create exceptions at all by doing its own spawning verification. This likely would be the hardest solution because there doesn't seem to be an easy way to get any common base that covers all the possible mobs that might spawn.

commented

Oh wow! Thank you very much for the detailed issue report. I didn't know mob spawning was multi-threaded... Are you using any other mods that might be modifying its logic? Either way, I should probably fix this. I'll take a look at your suggestions and properly test everything later when I got the time. Thanks again for all the details and time you must've spent looking at everything and trying to found out exactly what's wrong.

commented

I noticed an error with my code above. It should actually be:

        if(BigTorchBlockEntity.Companion.isChunkSuppressed(world.toServerWorld().getRegistryKey(), new ChunkPos(pos))) {
+            // Include checking the block beneath the current pos to see if it is a CursedDirt block
+            if !(world.toServerWorld().getBlockState(pos.down()).getBlock() is CursedDirt) {
+            // if the block isn't, then return false like before
                info.setReturnValue(false);
+            }
        }

This behavior means that in suppressed chunks, if not on CursedDirt then don't let any mobs spawn...otherwise check to see if light level is correct for spawning or normal behavior.

commented

I didn't know mob spawning was multi-threaded... Are you using any other mods that might be modifying its logic?

Since you brought it up, I loaded up a MultiMC instance with just Kibe, Fabric Kotlin, and Fabric API. Here is a picture I was able to get within 30 minutes of setting up a test:
Zombie and full Big Torch

If you would like a copy of the test instance, just let me know. I basically made a small area that has cursed dirt underground, a big torch above so no other mobs would spawn except the cursed dirt ones, chunkloaded that area, used conveyors to move any cursed mobs out of that chunk so the cap wouldn't be hit, and then went to a different area and fully loaded a big torch. The zombie appeared after a couple minutes. I did move between the two locations a couple times and did notice that at night there were more mobs spawning outside of the Big Torch radius. This is why I think it might be a concurrency issue, it seems that while it doesn't happen often, it can happen that a "normal" spawn check will hit before the cursed dirt check completed.

commented

Turns out it wasn't a concurrency issue after all. I was just forgetting to set the exception to false if the mob didn't meet the spawn requirements.