Is there something odd about block updates on chunk border blocks?
I'm raising sugar cane all around the edges of a small island and using this for paper to upgrade some librarians. I go across to harvest when I look across from my base and see all appear to be 3 blocks high.
Nearly every time, most of the cane along one side of the island, which lies on a chunk border, is only 2 blocks high though all the rest is 3 blocks. Only if I wait much longer (Double the time?) are the plants here all 3 blocks high. Are borders updated less often or is there another reason for this?
Barring any bugs or changes to how the game randomly chooses blocks, there shouldn't be any difference; it is true that they chose a very poor LCG (a type of random number generator) algorithm but the way it is used gives a variation on the order of only 0.04%:
A linear congruential generator is used to pick the blocks for updates. As Wikipedia explains, an LCG is often a poor choice for generating coordinates, as they tend to lie on a series of distinct planes. However, that only applies when sequentially generated values are used for the coordinates. Instead, Minecraft extracts 4-bit fields from a single generated value, and uses those for the x/y/z coordinates. I ran some simulations, and this appears to solve the problem of serial correlation: all blocks within a section received nearly-equal numbers of ticks (variation of only 0.04%).
This comment made by MCP isn't even correct; the coordinates are chosen per chunk section, 16x16x16; presumably, they never updated it to reflect the chunk format introduced in 1.2, which split chunks into sections, and the number of sections in a chunk has no effect on the randomness per section since they are all independent:
/**
* Contains the current Linear Congruential Generator seed for block updates. Used with an A value of 3 and a C
* value of 0x3c6ef35f, producing a highly planar series of values ill-suited for choosing random blocks in a
* 16x128x16 field.
*/
However, it is simply terrible if you use successive values, rather than extracting bits from a single value, as seen in these comparisons of 32 bit LCGs, one using the formula that the game uses and the other using one that I use in my own code (the vertical lines are divisions between layers, where each layer is a 16x16 slice of a chunk section, and the brightness values are scaled from 0-255 based on the full range of counts each point accumulated):
Similar misuse of an LCG may also explain why seeds with the same lower 48 bits show some similarities in biomes (last spoiler) despite the use of a 64 bit LCG (the period of any bit in a power of 2 LCG is 2^n, so the lowest bit has a period of 2 and the highest bit has a period of 2^64, and as a result you want to extract the highest bits. The biome generator only discards the lower 24 bits so nextInt(2) has a period of only 2^25).
The random block tick logic only discards the lowest 2 bits so the result isn't very random even if every block is ticked more or less equally (in particular, the x-coordinate has a period of 64, z 16384 and y 4194304), although this probably isn't very noticeable since it applies more to consecutive blocks within a section and only 3 are chosen per tick and there is only a chance of many blocks responding (sugarcane has 16 growth stages, starting from 0 and growing once it is ticked with a stage of 15):
for (int i = 0; i < 3; ++i)
{
this.updateLCG = this.updateLCG * 3 + 1013904223;
int x = this.updateLCG >> 2 & 15;
int z = this.updateLCG >> 10 & 15;
int y = this.updateLCG >> 18 & 15;
Block block = Block.blocksList[cs.getBlockID(x, y, z)];
if (block != null && block.getTickRandomly())
{
block.updateTick(this, x + chunkX, y + sectionY, z + chunkZ, this.rand);
}
}
For comparison, this is what the randomness map looks like; the variation was about 0.08% for an average of 100000 counts per point for either algorithm (for comparison, using successive values had a variation of about 0.5% for the better one, and was also noticeably slower, hence why they may have decided to use a home-brew LCG instead of the Random class that Java has, which is far slower than either method):
I suppose it biases towards the center of a chunk, kind of like how cage spawners spawn mobs more towards the cage than towards the edges of the spawn area. If the block selection is limited similarly to natural mob spawn attempts (1 attempt per chunk per unit time, in the case of hostiles it's once per tick).
I suppose it biases towards the center of a chunk, kind of like how cage spawners spawn mobs more towards the cage than towards the edges of the spawn area. If the block selection is limited similarly to natural mob spawn attempts (1 attempt per chunk per unit time, in the case of hostiles it's once per tick).
Random block updates are completely different from mob spawning; the code I showed, even if from 1.6.4, is likely still relevant - the game chooses 3 random points (that's what "randomTickSpeed" refers to, which defaults to 3, while in 1.6.4 it was hard-coded in) per chunk section per tick (that's up to 48 per chunk, times the number of loaded chunks, excluding empty sections or sections without blocks that respond to random ticks):
Chunks consist of sixteen so-called sections, each one a 16×16×16=4096 block cube. Three (or set by /gamerule randomTickSpeed) block positions are chosen at random from each section in the chunk. The blocks at those positions are given a "random tick". Most blocks ignore this tick, but some use it to do something:
Also, I was able to find the code that 1.15 uses, which was heavily abstracted away (the complexity of 1.15 in terms of the number of classes is quite ridiculous, of course, this has been the case since 1.8) but is functionally the same as that for 1.6.4, down to the same LCG algorithm (note that I cleaned up the 1.6.4 code a bit, removing an intermediate variable which was set to updateLCG >> 2, in which case the last line would be "var1 + (var5 >> 2 & 15), var2 + (var5 >> 18 & var4), var3 + (var5 >> 10 & 15)"):
for(int var13 = 0; var13 < var2; ++var13)
{
fk var14 = this.a(var5, var12, var6, 15);
var7.a("randomTick");
byg var15 = var22.a(var14.o() - var5, var14.p() - var12, var14.q() - var6);
if (var15.q())
{
var15.b(this, var14, this.o);
}
public fk a(int var1, int var2, int var3, int var4)
{
this.i = this.i * 3 + 1013904223;
int var5 = this.i >> 2;
return new fk(var1 + (var5 & 15), var2 + (var5 >> 16 & var4), var3 + (var5 >> 8 & 15));
}
ETA: I did a further test, cycling through all 2^32 states and the result was perfectly uniform, exactly 1048576 counts per block.
Random block updates are completely different from mob spawning; the code I showed, even if from 1.6.4, is likely still relevant - the game chooses 3 random points (that's what "randomTickSpeed" refers to, which defaults to 3, while in 1.6.4 it was hard-coded in) per chunk section per tick (that's up to 48 per chunk, times the number of loaded chunks, excluding empty sections or sections without blocks that respond to random ticks):
Also, I was able to find the code that 1.15 uses, which was heavily abstracted away (the complexity of 1.15 in terms of the number of classes is quite ridiculous, of course, this has been the case since 1.8) but is functionally the same as that for 1.6.4, down to the same LCG algorithm (note that I cleaned up the 1.6.4 code a bit, removing an intermediate variable which was set to updateLCG >> 2, in which case the last line would be "var1 + (var5 >> 2 & 15), var2 + (var5 >> 18 & var4), var3 + (var5 >> 10 & 15)"):
for(int var13 = 0; var13 < var2; ++var13)
{
fk var14 = this.a(var5, var12, var6, 15);
var7.a("randomTick");
byg var15 = var22.a(var14.o() - var5, var14.p() - var12, var14.q() - var6);
if (var15.q())
{
var15.b(this, var14, this.o);
}
public fk a(int var1, int var2, int var3, int var4)
{
this.i = this.i * 3 + 1013904223;
int var5 = this.i >> 2;
return new fk(var1 + (var5 & 15), var2 + (var5 >> 16 & var4), var3 + (var5 >> 8 & 15));
}
ETA: I did a further test, cycling through all 2^32 states and the result was perfectly uniform, exactly 1048576 counts per block.
Good one. Thanks for sharing a nice piece of info.
Is there something odd about block updates on chunk border blocks?
I'm raising sugar cane all around the edges of a small island and using this for paper to upgrade some librarians. I go across to harvest when I look across from my base and see all appear to be 3 blocks high.
Nearly every time, most of the cane along one side of the island, which lies on a chunk border, is only 2 blocks high though all the rest is 3 blocks. Only if I wait much longer (Double the time?) are the plants here all 3 blocks high. Are borders updated less often or is there another reason for this?
Learn something new each day
Barring any bugs or changes to how the game randomly chooses blocks, there shouldn't be any difference; it is true that they chose a very poor LCG (a type of random number generator) algorithm but the way it is used gives a variation on the order of only 0.04%:
This comment made by MCP isn't even correct; the coordinates are chosen per chunk section, 16x16x16; presumably, they never updated it to reflect the chunk format introduced in 1.2, which split chunks into sections, and the number of sections in a chunk has no effect on the randomness per section since they are all independent:
However, it is simply terrible if you use successive values, rather than extracting bits from a single value, as seen in these comparisons of 32 bit LCGs, one using the formula that the game uses and the other using one that I use in my own code (the vertical lines are divisions between layers, where each layer is a 16x16 slice of a chunk section, and the brightness values are scaled from 0-255 based on the full range of counts each point accumulated):
state = state * 3 + 1013904223:
state = state * 1664525 + 1013904223:
index = nextInt(16) | nextInt(16) << 4 | nextInt(16) << 8
Similar misuse of an LCG may also explain why seeds with the same lower 48 bits show some similarities in biomes (last spoiler) despite the use of a 64 bit LCG (the period of any bit in a power of 2 LCG is 2^n, so the lowest bit has a period of 2 and the highest bit has a period of 2^64, and as a result you want to extract the highest bits. The biome generator only discards the lower 24 bits so nextInt(2) has a period of only 2^25).
The random block tick logic only discards the lowest 2 bits so the result isn't very random even if every block is ticked more or less equally (in particular, the x-coordinate has a period of 64, z 16384 and y 4194304), although this probably isn't very noticeable since it applies more to consecutive blocks within a section and only 3 are chosen per tick and there is only a chance of many blocks responding (sugarcane has 16 growth stages, starting from 0 and growing once it is ticked with a stage of 15):
For comparison, this is what the randomness map looks like; the variation was about 0.08% for an average of 100000 counts per point for either algorithm (for comparison, using successive values had a variation of about 0.5% for the better one, and was also noticeably slower, hence why they may have decided to use a home-brew LCG instead of the Random class that Java has, which is far slower than either method):
TheMasterCaver's First World - possibly the most caved-out world in Minecraft history - includes world download.
TheMasterCaver's World - my own version of Minecraft largely based on my views of how the game should have evolved since 1.6.4.
Why do I still play in 1.6.4?
I suppose it biases towards the center of a chunk, kind of like how cage spawners spawn mobs more towards the cage than towards the edges of the spawn area. If the block selection is limited similarly to natural mob spawn attempts (1 attempt per chunk per unit time, in the case of hostiles it's once per tick).
Random block updates are completely different from mob spawning; the code I showed, even if from 1.6.4, is likely still relevant - the game chooses 3 random points (that's what "randomTickSpeed" refers to, which defaults to 3, while in 1.6.4 it was hard-coded in) per chunk section per tick (that's up to 48 per chunk, times the number of loaded chunks, excluding empty sections or sections without blocks that respond to random ticks):
Also, I was able to find the code that 1.15 uses, which was heavily abstracted away (the complexity of 1.15 in terms of the number of classes is quite ridiculous, of course, this has been the case since 1.8) but is functionally the same as that for 1.6.4, down to the same LCG algorithm (note that I cleaned up the 1.6.4 code a bit, removing an intermediate variable which was set to updateLCG >> 2, in which case the last line would be "var1 + (var5 >> 2 & 15), var2 + (var5 >> 18 & var4), var3 + (var5 >> 10 & 15)"):
ETA: I did a further test, cycling through all 2^32 states and the result was perfectly uniform, exactly 1048576 counts per block.
TheMasterCaver's First World - possibly the most caved-out world in Minecraft history - includes world download.
TheMasterCaver's World - my own version of Minecraft largely based on my views of how the game should have evolved since 1.6.4.
Why do I still play in 1.6.4?
Good one. Thanks for sharing a nice piece of info.
My Wegmans Connect