[SPIGOT-7159] Crop Growth Modifier resolution is severely low. Created: 17/Sep/22 Updated: 25/Dec/24 Resolved: 19/Oct/22 |
|
| Status: | Resolved |
| Project: | Spigot |
| Component/s: | None |
| Affects Version/s: | None |
| Fix Version/s: | None |
| Type: | Bug | Priority: | Minor |
| Reporter: | Jordan Stevens | Assignee: | Marvin Rieple |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | 1.19, bug, spigot | ||
| Attachments: |
|
| Version: | 1.19-R0.1-SNAPSHOT |
| Guidelines Read: | Yes |
| Description |
|
For years growth rate issues have plagued spiggot. Where the modifiers seemingly did not effect the server if increased and required significant decrease to reduce. Today I have discovered why. Within "BlockCrops.randomTick", there is an if statement as follows: if (randomsource.nextInt((int)(100.0F/ (float)modifier * (25.0F / f)) + 1 ==0) This is catastrophically wrong. The vanilla probability of crop growth is 1/(25 / f +1) A modifier to this chance would be 100/Modifier[1/(25 / f +1)]{*}with 100 being normal growth. This is NOT what occurs in the if statement above. (int)(100.0F/ (float)modifier * (25.0F / f)) is where the problem lies. This value will always come out as an integer rounded down. I hope it is clear to see that, say for a case of modifier = 100 and f = 10 that (int) 100.0F / (float)100 * (25.0F/10) < 1 and hence = 0. Hence, most cases (until modifier is equal to or less than 40) the growth chance will always be based on: if (randomsource.nextInt(0 + 1 ==0) Hence a 50% chance as a random int between 0 and 1 are chosen. If 0 , then true. This is a bug that, considering forum posts about the issue, has been persistent for years. As such I will offer my personal recommendation at a fix below: int chancevanilla = (int)(100/(floor(25.0F/f)+1))-1; //%chance in vanilla MC int ModMult = (int)(modifier/100.0F); int chancemod = (int)(chancevanilla*ModMult; if((randomsource.nextInt(99) <= chancemod) // rolls a random integer between 0 and 99 (100 numbers).// If chancevanilla or lower is rolled, then true. I.e. if chance vanilla == 33 and then the modifier was 200, then chancemod would be 65 and hence have a 66% chance of success (66 choices lead to a true if statement out of the potential 100 ints between 0 and 99) |
| Comments |
| Comment by Marvin Rieple [ 30/Oct/22 ] |
|
Thanks for the notice, made a PR to fix this: spigot#122 |
| Comment by Bobcat00 [ 30/Oct/22 ] |
|
You're missing parentheses. As written, the division only applies to melonModifier, not pumpkinModifier. if (randomsource.nextFloat() < (this == Blocks.PUMPKIN_STEM ? worldserver.spigotConfig.pumpkinModifier : worldserver.spigotConfig.melonModifier / (100.0f * (Math.floor((25.0F / f) + 1))))) { // Spigot - SPIGOT-7159: Better modifier resolution |
| Comment by Marvin Rieple [ 16/Oct/22 ] |
|
Made a PR for this: spigot#121 |
| Comment by Jordan Stevens [ 23/Sep/22 ] |
|
Oh my 1000 apologies. I see now. To be blunt. That is elegant and effective. I have never reported an issue before, but I do hope your solution is implemented! :O |
| Comment by Marvin Rieple [ 23/Sep/22 ] |
|
f = 10 modifier = 100
(modifier / (100.0f * (Math.floor((25.0f / f) +1)))) =(100 / (100.0f * (Math.floor((25.0f / 10) + 1)))) =(100 / (100.0f * (Math.floor((2.5) + 1)))) =(100 / (100.0f * (Math.floor(3.5)))) =(100 / (100.0f *3)) =(1 / 1 * 3) =(1 / 3) = 0.3333...
100 / (100 * x) = 1 / (1 * x) = 1 / x 100 / (100 * x) != x |
| Comment by Jordan Stevens [ 22/Sep/22 ] |
|
Erm... okay not sure how you mean f = 10 modififer = 100 (modifier / (100.0f * (Math.floor((25.0f / f) + 1) = 1*Math.floor(25/10 + 1) = 3 if (randomsource.nextFloat() < 3) Yeah that aint scaled down at all chief. That is literally an if statement that rings true if a random float between 0 and 1.0 is less than 3... |
| Comment by Marvin Rieple [ 21/Sep/22 ] |
And unfortunately that will not work as random.nextFloat() chooses a random float between 0 and 1.0. This is why the value gets scaled down between 0 and 1. |
| Comment by Jordan Stevens [ 19/Sep/22 ] |
|
No i got the initial problem wrong haha And unfortunately that will not work as random.nextFloat() chooses a random float between 0 and 1.0. My method still has a resolution problem, but guarantees a resolution similar to that of what is currently seen for sub 100 modifiers. And hence is an acceptable solution |
| Comment by Marvin Rieple [ 18/Sep/22 ] |
|
I think I misunderstood your original post. But I think to get a better resolution it would be better to remove the nextInt part completely and use nextFloat. if (randomsource.nextFloat() < (modifier / (100.0f * (Math.floor((25.0f / f) + 1))))) Your proposed fix, also suffers from int casting since all intermediary steps are ints. See blue graph. |
| Comment by Jordan Stevens [ 17/Sep/22 ] |
|
To conclude as that last note got very big very fast... There is 0 way to get any growth rate that is between 51% and 99%
Your choices with the current system, past ideal maximum in vanilla, are 33% 50% or 100% |
| Comment by Jordan Stevens [ 17/Sep/22 ] |
|
"If we now input the lowest and highest possible f value into the vanilla formula we get value between: (25 / 10) = 2.5 and (25 / 0.5) = 50" With all due respect... you are wrong. randomsource.nextInt * so to change your from: So if i am not being clear enough, due to the int casting, the resolution for increasing modifier for modifier > 100 is, quite frankly, garbage. E.g: 100 modifier = 100/100 * 25/10 = (int)(2.5)+1 = 2+1 = 3 : 33% etc etc etc The resolution isnt reduced, its pretty much non existant between 100 and 126. This gets worse when you then consider the same growth rate occurs between 126 and 250.... 50%.... An increase of the modifier from 126 to 200... would still only net you a 50% growth chance. The argument to be made here is that the reason so many people have stated online "Oh i increased the modifier by another 50% and it didn't do anything) is exactly due to this. To full visualise the issue... see the graph and table below. As you can see it causes a little to 0 effect at all part the 125 mark. (And infact a modifier of 245 would only increase the growth rate by
And to really hit the nail home... For a growth speed of 5... there are bad resolutions.... and there is increasing a 17% base chance by 400% and only getting 50 % ...
|
| Comment by Marvin Rieple [ 17/Sep/22 ] |
|
I don't see anything wrong with how the modifier is implemented. The default vanilla calculation is: (As can be seen: here and here
(int) (25.0F / f) + 1
And looking at the getGrowthSpeed speed method f can be between 10 and 0.5. For simplicity we ignore the + 1. If we now input the lowest and highest possible f value into the vanilla formula we get value between: (25 / 10) = 2.5 and (25 / 0.5) = 50 ((100.0F / modifier) * (25.0F / f)) As seen in the code 100/modifier implies that the modifier in the config gives the growth rate in % where 100% is normal speed, 50% is half the speed, and 200% is double the speed. We can now also look at the case where the modifier is 200, there we have 100/200 = 0.5 which gives us values between 1.25 and 25. Which half's the possible values random can be and therefore doubles the chance of hitting. There could be an argument made that because of the int casting the resolution of the modifier gets worse the larger the modifier goes, but in general there is nothing wrong with the implementation. |
| Comment by Jordan Stevens [ 17/Sep/22 ] |
|
Im actually an idiot and forgot that Random.nextInt(int n) is exclusive to n Still think this would give more versatility to the modifiers as it would allow for values other than just 33.3 , 50 and 100 percent via said modifiers. int chancevanilla = 100/((int)(25.0F/f)+1); //%chance in vanilla MC int ModMult = (int)(modifier/100.0F); int chancemod = (int)(chancevanilla*ModMult; if((randomsource.nextInt(100) <= chancemod) // rolls a random integer between 0 and 99 (100 numbers).// If chancevanilla or lower is rolled, then true. In addition, because the modifier is not independent of the in game modifiers, by increasing the growth modifier you risk making the in game modifiers worthless. E.g: A modifier of 10000 would make farms grown with 0 water as fast as farms with water. I.e. a modifier with a limit of 100% has many flaws. |