The bug can be reproduced on a fresh world in 1.21.10.22. It isn't related to whether the world is upgraded or not. The game will create structure spawning areas (hard coded spawning areas) in fresh chunks, but it doesn't seem to save them or load them from existing chunks. This is very bad because once the hardcoded spawning areas are lost, structure spawning will also be permanently lost from those chunks.
Create a new world with seed 1.
TP to 3200 100 1800 and you will find a SwampHut there.
Tear down the roof and wall of the hut and build a platform to cover the entire chunk.
Kill all monsters `/kill @e[family=monster]`
Watch witches spawn near the center of the SwampHut
Restart the game. Kill all monsters again. Witches no longer spawn as they did before.
I inspected the world save using rbedrock and determined that the information for hardcoded spawning areas (chunk data with tag 57) isn't being saved in the world db. The information also does not appear to be moved to another location in the world db. Therefore it appears that the LevelChunk class has a regression that dropped support for saving and loading hardcoded spawning areas.
Dimension::tickRedstone() is the function responsible for evaluating redstone circuits every other game tick. It roughly looks like this:
if( redstone_counter >= redstone_counter_max) THEN
evaluate circuit system
redstone_counter = 0
endif
redstone_counter += 1 // Redstone counter is always either 1 or 2.
In the Dimension::Dimension() constructor both redstone_counter and redstone_counter_max are initialized to 2, such that the circuit system will be evaluated on the first time `Dimension::tickRedstone()` is called after startup. This results in a 50/50 chance that a server will evaluate redstone circuits on even or odd ticks, depending on what the game time values is when a server restarts.
You can see this happen in game by triggering a command block to run the command "time query gametime" on a redstone pulse. Sometimes it will output even number and othertimes it will output odd numbers.
For any redstone components that utilize pending ticks or require precise timing, changing the parity of redstone ticks across restarts can cause them to behave in undefined ways.
The solution is likely simple: initialize redstone_counter to 2 or 1 based on whether the game time is odd or even. This would ensure that redstone ticks are synchronized across restarts.
Below are the results of code-digging and comparing 1.19.72 BDS to 1.20.12 BDS.
In 1.19.72, the environmental spawning algorithm [ Spawner::spawnMob() ] and the iron golem spawning algorithm [SpawnUtils::trySpawnMob()] used UpdateBoundingBoxSystem:updateBoundingBoxFromDefinition() to update a mobs bounding box before collision checks.
In 1.20.12, the environmental spawning algorithm does something else while the iron golem spawning algorithm still uses UpdateBoundingBoxSystem:updateBoundingBoxFromDefinition().
I suspect that UpdateBoundingBoxSystem:updateBoundingBoxFromDefinition() broke in 1.20.10.x previews and the environmental spawning system was later patched to use something else. However, the iron golem spawning algorithm hasn't been patched yet.
I did some additional testing and it appears that, for partial blocks, the preferred block costs are calculated based on the block underneath the partial block. I created a test world with a simple village. The village contains one fletcher, one fletching table, and one bed. When time is set to night, the villager will go to his bed. When time is set to day, the villager will go to his workstation.
Expected Behavior: The villager walks in a straight line between the bed and workstation. Dirt paths all have the same cost (cost of 0) and the best path should be a straight one between the bed and workstation.
Observed behavior: He doesn't take a direct route. Instead he follows the route of the planks below the dirt paths (cost of 1). He avoids walking on the dirt paths that are above blast furnaces (cost of 50).
[media]
Steps to Reproduce:
Create a new flat world with mob spawning off.
Use spawn eggs to create 10 llamas. Kill all the llamas.
Save game.
Use an external tool like rbedrock or whatever mojang has internally to inspect the world save.
Count how many "actorprefix" keys there are in the leveldb.
Count how many actors are stored in digp records in the leveldb.
If the count from step 5 is greater than the count from step 6, you have reproduced this bug.
Observed Result:
There are 10 actorprefix keys. Each one holds NBT data for a llama. None of these keys are referenced in any digp record.
Expected Result:
There are 0 actorprefix keys.
I used rbedrock to analyze a fresh 1.19 world that was run for about 20 minutes or so.
I can confirm that there are actor keys in the database that are not recorded in any chunk's actor digest record. If those keys are from dead or despawned mobs, then the database has a "memory" leak.
I haven't tested if the leak keeps growing the longer the world is simulated. So I don't know if these orphaned actors will eventually be garbage collected.
I use turtle eggs in my drowned farm, and I am unable to replicate this bug in 1.18.30. I think this bug is invalid.
I created a simple test world.
[media]In this world is a pen containing a drowned, husk, zombie, and zombified piglin. To verify that turtle eggs attract these mobs, place one on any of the emerald blocks. As expected, if (1) the turtle egg is in the detection range of the mobs, (2) there is an air-block above the turtle egg, and (3) the mobs an find a path to the egg, they will be attracted to it. There is no delay in the attraction.
I have viewed the video claiming to show this bug, and that video is consistent with expected mechanics. When the mobs can't find a path to an egg, they aren't attracted to it. When they can find a path to the egg, they go right for it.
I figured out what changed in 1.18.30 that broke creeper farms.
Code Analysis
When spiders (and most mobs) spawn, the game will cancel the spawning attempt if the mob collides with any block that has an AABB (axis-aligned bounding box). The function used for this is the virtual function BlockLegacy::getAABB()
or its overrides. A refactoring occurred between 1.17 and 1.18 that added a boolean parameter to this function. I'll call it param_5
. When param_5
is set to false
, many blocks return an empty AABB. This is likely used to distinguish different types of AABBs, such as collision boxes or hit boxes. Notably param_5
is set to false
when the game checks if a mob collides with any block.
In 1.18.30, ButtonBlock::getAABB()
has been added to the game. (Buttons used BlockLegacy::getAABB()
in earlier versions.) The dev who wrote this function included logic that returns an empty AABB if param_5
is false
. This returns an empty bounding box during mob spawning, causing buttons to stop blocking spider spawns. This isn't a minor inconvenience. Even a small creeper farm can have thousands of buttons that would need to be replaced by an alternative block (if one even exists).
The Fix
Remove the logic in ButtonBlock::getAABB()
that returns an empty bounding box if param_5
is false
. ButtonBlock::getAABB()
should always return an accurate bounding box regardless of the value of param_5
. Given how quickly changes to pathfinding on coral fans was fixed, I hope that this bug can be quickly fixed as well.
I too can confirm this bug on Windows 10. I tested a world that only contains a creeper farm, and spiders are spawning in the farm.
I suspect that the way the collision box is calculated for buttons has changed in 1.19 preview.
I experienced this same problem on a BDS world. A copy of the world can be found here: https://www.dropbox.com/s/3fwf4uzvo6yziln/ClassSMP-trim.zip?dl=0
This was was created last week in 1.17.34. When 1.17.40 came out on Wednesday, I upgraded the sever and enabled the Caves-N-Cliffs experimental toggle. Over the weekend, I noticed that many of the mobs spawner's nbt data had missing `EntityIdentifiers`, and thus have no entity spinning in the mob spawner and don't spawn mobs when the player gets close.
A table of all locations of mob spawners in this world is attached if anyone needs to visually inspect the world:
[media].
Code Analysis
In `Level::tickBlocks()` the game uses signed integer arithmetic to generate the positions of random blocks to tick in a chunk. As a result, the random y level is negative 50% of the time, and BE selects a random block of air underneath the chunk instead of a block in the chunk. This causes the observed loss of 50% of random ticks.
A Proposed Fix
Use unsigned integer arithmetic to generate the positions of random blocks.
After reverse engineering iron golem spawning mechanics in 1.17.30. And testing different designs in 1.17.40+ betas, I have developed the following improvement to the iron golem spawning mechanics.
In this algorithm, iron golems will spawn on top of the highest solid block in the search column that is below an air or liquid block also in the search column. If the spawn location is obstructed by blocks or entities, the spawn will fail and another attempt will be made.
// C++ style psuedocode to improve MobSpawnUtils::trySpawnMob()
for(int i = 0; i < num_attempts; ++i) {
auto current_pos = village_center;
// Draw a random x and z offsets uniformly between -xz_offset and xz_offset.
// The lower and upper limits are inclusive.
current_pos.x += random_int_inclusive(-xz_offset, xz_offset);
current_pos.z += random_int_inclusive(-xz_offset, xz_offset);
// Look for the highest gap block in spawning area.
// A gap block is a air block or a liquid block.
int y_off = y_offset;
for(; y_off > -y_offset; --y_off) {
current_pos.y = village_center.y + y_off;
auto current_block = block_source.getBlock(current_pos);
if(current_block == mAir || current_block.getMaterial().isLiquid()) {
break;
}
}
// No gap block found. Search failed, try again
if(y_off <= -y_offset) {
continue;
}
// Look for highest solid block below the gap block
for(; y_off > -y_offset; --y_off) {
current_pos.y = village_center.y + y_off;
auto platform_block = block_source.getBlock(current_pos.below());
if(current_block.isSolid()) {
break;
}
}
// No solid block found. Search failed, try again
if(y_off <= -y_offset) {
continue;
}
// Try creating a golem.
auto mob = create_golem(current_pos);
if(mob->checkSpawnRules() && mob->checkSpawnObstruction()) {
// golem successfully spawned
return mob;
}
// Golem spawning is blocked. Destroy golem and try again.
destroy_golem(mob);
}
return null;
Code Analysis
Between 1.16.100 and 1.17.0, the lightning code was refactored. In 1.16.100, lightning bolts and skeleton traps were simulated separatly. Lightning bolts only struck the surface, while traps could strike and entity or the surface. This separation was probably due to a patch made to fix the bug mentioned in this report. However, during the refactoring, bolts and traps are now linked in 1.17.0, causing this bug to reemerge.
Lightning is spawned by LevelChunk::tickBlocks()
. This function does a lot of things, but I'll focus only on the lightning spawning algorithm. The algorithm looks roughly like this
if(isLightning() == false) {
goto END
}
if(randInt(100000) != 0) {
goto END
}
pos = randBlockpos()
target = findLightningTarget( pos )
target_biome = getBiome(target)
if( isRaining(target_biome) == true && isColdBiome(target_biome) == false ) {
trySpawnSkeletonTrap(target)
spawnLightning(target)
}
END:
Now findLightningTarget(pos)
can find three different targets
(1) the top rain block in the same vertical column as pos.
(2) a random entity (with entity type 0x1300) in the chunk
(3) a lightning rod near position (1)
The "top rain block" is the block right above the highest block in the column with a material that blocks precipitation.
A lightning rod can only be found if it can see the sky and is getting rained on.
If a random entity is chosen the game does NOT check to see if it can see the sky and is getting rained on. This is why people's pets are getting struck by lightning deep under ground.
Before refactoring, the game would double check that the result of findLigntningTarget(pos)
was valid, but this was eliminated in the refactoring, exposing the bug.
*Suggested Fix*
After a random entity is chosen, the game should check that it can see the sky and is getting rained on similar to a lightning rod. If the check fails, the game should fall back to using the random surface block defined by (1).
Code Analysis: In 1.16.100, the y-level for slime spawning was changed to consider sea level. Specifically, slimes now spawn in slime chunks up to 23 blocks below sea level. On normal worlds, sea level is at y=63 and on flat worlds sea level is at y=5. Clearly this eliminates slime spawning in slime chunks on flat worlds.
Potential Fix: Make sea level data driven. If it was customizable in level.dat, then marketplace creators who create using flat worlds can change the value if they need too.
I investigated the differences in diamond ore generation between 1.16.221 and 1.17.0. I generated fresh worlds with the same seed in both versions and used rbedrock to compare 49 chunks centered around the world spawn in both versions. I then identified all locations that where diamond ores in either world (both normal and deepslate) in either world. I then grabbed the blocks in these locations in both world and constructed a table to highlight the differences.
[media]
A couple observations:
The distribution of ores has not changed between versions. If it had changed you would see locations in 1.17 that had diamond ores that didn't have ores in 1.16. But you don't. This is confirmed by looking at the jsons for diamond ore features, which still creates one ore cluster per chunk between y=0 and y =15.
However, 64% of the diamond ore blocks have gone missing, and in their place is stuff like stone (which they should replace according to the jsons) and deepslate (which they don't replace according to the jsons, but maybe they should).
My guess on what is happening: deepslate is causing ore generation to stop. So when the algorithm encounters deepslate, it stops trying to place diamonds. This is why some stone blocks are not being converted into diamonds.
Update: I was looking in the wrong location for feature descriptions. Turns out the in 1.17.0 diamond ore veins were made smaller (4 blocks instead of 8 blocks). But this has been fixed in 1.17.10.
I would like to add that the availability of symbols in the Linux BDS version of the game allows code diggers like myself to add in depth code analysis to bug reports making it easier to identify and fix the causes of bugs.
Code Analysis (BDS 1.16.100): The code analysis in the report is incorrect. Those two functions are not responsible for ticking chunks.
`TickingAreaView::tick()` is the important function to pay attention to. This is the function that gets called every game tick to tick all the chunks in a ticking area. There is a bool flag passed to `TickingAreaView::tick()`, and that function does something different depending on whether the flag is TRUE or FALSE. I believe it's this flag that is responsible for chunks in ticking areas sometimes not ticking.
`TickingAreaListBase::tick()` ticks all the ticking areas in a dimension and is responsible for setting the flag that gets passed to `TickingAreaView::tick()`. For each ticking area, this flag is true once every 20 ticks, but not all ticking areas will have the flag set at the same time. (I'm simplifying here.)
When the flag is false, all chunks in the ticking area appear to be ticked normally. When the flag is true, the ticks seem to be getting ticked in a random order, or only a random subset get ticked. But I'm not quite sure of this.
My best guess is that ticking areas are supposed to work like this, but in practice they do not:
(1) Every tick: Tick all chunks in ticking area.
(2) Every 20 ticks, randomize the order of chunks in the ticking area.
This is a code analysis based on 1.16.100 (i.e. before the pending/random ticks system was updated) and a suggestion on how to reimplement portal ticking gold farms in 1.17.
1.16.100: Portal Ticking gold farms worked because one function PortalBlock::tick() was responsible for both testing if a portal was valid on a block update and spawning zombie pigment on a random tick. Basically before 1.17, a block could be ticked randomly and non-randomly and the same set of random and non-random things would happen when a block was ticked. The way active-portal gold farms work is to light and break portals repeatedly to force PortalBlock::tick() to be called, spawning zombie pigmen. It's honestly a really cool mechanic and OP the same way Java's nether roof pigmen farms are OP. On Hard difficulty, the probability that a portal-block spawns a zombie pigman when ticked is 3/2000. For a maximum sized portal, this means that there is an average of 0.66 zombie pigmen spawned per portal lighting. Note: The number of zombie pigment spawned per portal lighting follows a binomial distribution and is well approximated by a Poisson distribution.
Potential 1.17 Fix: Active-portal gold farms can be supported without reverting the existing patches in 1.17. The game needs a specific mechanic where zombie pigmen have a chance to spawn if a portal is lit. This can be done on based on individual portal blocks when they are lit, or by the portal itself based on its size when lit. 1.17 does not need to model existing binomial/poisson distribution of zombie pigmen spawning rates, as long as the average is kept the same. Thus I propose the following mechanic:
When a portal is lit, the chance that a zombie pigman will spawn is height*width*difficulty/2000, where difficulty is 0 (peacful) to 3 (hard) and height/width measure the dimensions of the portal, not including the obsidian border.
The Linux Loader has been working on creating a patch for this issue. I'm posting the link here because it might help someone at Mojang track down the issue on their end.
Mojang appears to have recently ported over biome tags from Java to Bedrock. Several of these tags don't make sense and are irrelevant to Bedrock such as "polar_bears_spawn_on_alternate_blocks" and "without_zombie_sieges". Other tags might be useful for modernizing Bedrock's mob spawning rules, making them more readable by Java devs, and/or make it easier for addons to query biome information. Unfortunately, during the porting process, the following Java code was incorrectly parsed, causing erroneous tags to be added to mangrove swamps, which was not caught when the changes were reviewed.
Proposed Fix
Only the following tags should be in definitions/biomes/mangrove_swamp.biome.json:
allows_surface_slime_spawns
mangrove_swamp
overworld
spawns_warm_variant_frogs