Added new local mob count feature for per-player mob caps in spawn.json rules - #565
Open
PizzaConBacon wants to merge 3 commits into
Open
Added new local mob count feature for per-player mob caps in spawn.json rules#565PizzaConBacon wants to merge 3 commits into
PizzaConBacon wants to merge 3 commits into
Conversation
Author
|
The performance impact is minimal, though I must mention that the complexity is not O(1) like in Paper's implementation but O(N) where N is the number of different radii used in the rules. So 10 rules that each uses the default 'maxlocaldist' value would still perform as fast as Paper's patch but say just 4 rules with chunk radii of 0, 3, 8, 2 and one extra 1 for an inner radius to make a donut shape in one of them for example, would increase by 5 times the amount of calculations. It is therefore recommended to reuse the radii for as many rules as possible. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the implementation of what is effectively described in this issue. See #563.
A new 'perlocal' keyword is added along 'minlocaldist' and 'maxlocaldist' for spawn.json rules. Here's an example rule that uses these:
{ "dimension": "minecraft:overworld", "hostile": true, "mincount": { "amount": 70, "hostile": true, "perlocal": true, "minlocaldist": 10, "maxlocaldist": 50 }, "result": "deny" }This rule is evaluated each time a hostile mob tries to spawn in the overworld. When that happens, we look for the players in range of this spawn and for each one of them we count how many hostile mobs around them there are. If any of the players have 70 or more hostile mobs around them, then the spawn is denied.
The range at which the hostile mobs are counted is defined by 'minlocaldist' and 'maxlocaldist'. With default values of 0 and 120 respectively (0 to start counting from the chunk that the player is already in and 120 because that's the default 'maxdist' value for spawner.json rules), they describe a donut shape around the player with two rings of radius in blocks, although internally they get converted into radii in chunks as the implementation is based on the extremely performant Paper server software 'per-player mob spawns' feature.
In this example, the mobs are counted starting from block 10 (Since a chunk is 16x16 blocks and we expect the player to be in the middle:
Math.floor((10 + 8) / 16) = 1so 1 chunk away from the player, not the one they're in) up to block 50 (Math.floor((50 + 8) / 16) = 3so 3 chunks away from the player)Behind the scenes, 'minlocaldist' and 'maxlocaldist' are two circles, to get the donut shape, to the 'maxlocaldist' circle is subtracted the 'minlocaldist' one.
When 'minlocaldist' is less than 8 (1 chunk), nothing is subtracted so 'maxlocaldist' is used as the complete radius.
This next example denies all spawns when there are 10 or more mobs in a 2 chunk radius around the player:
{ "mincount": { "amount": 10, "perlocal": true, "maxlocaldist": 24 }, "result": "deny" }When using 'maxcount', the behaviour is inversed as expected. This rule is similar to the previous one where it will only fire whenever there are no players in range with 10 or more mobs around a 2 chunk radius.
{ "maxcount": { "amount": 10, "perlocal": true, "maxlocaldist": 24 }, "result": "default" }Let's see one final example:
{ "spawntype": ["spawner"], "hostile": true, "mincount": { "amount": 100, "hostile": true, "perlocal": true, } "result": "deny" }This rule will deny all monster spawns from spawners when there are 100 or more hostile creatures around the players in an 8 chunk radius (default of 120 blocks
Math.floor((120 + 8) / 16) = 8). This is useful to avoid mob farm lag while not blocking spawners for everybody else in the dimension by using a global rule.The 'perlocal' implementation is chunk aware, which means that when two players are close to each other and unlike the 'maxlocal' keyword behaviour in spawner.json rules, it does not ignore shared chunks between players and therefore makes it so that the rule will fire when any of the players has reached the mob count within those chunks.
This fixes the problem of overcrowding when trying to achieve these per-player mob caps using 'maxlocal', because as a side effect of not taking shared chunks with other players into account when counting, two players can have overlapping chunks while not going over the cap and forcing the rule to spawn extra mobs making those chunks overcrowded.
We can explain it with this example (For the sake of simplicity, we are gonna ignore the player's current chunk):
{ "mob": "minecraft:zombie", "attempts": 20, "amount": { "minimum": 1, "maximum": 3 }, "conditions": { "dimension": "minecraft:overworld", "maxlocal": 50, "maxdist": 36 } }This rule will spawn zombies if the player closest to the spawn position does not have more than 50 zombies around them in a 36 block radius (
Math.floor((36 + 8) / 16) = 2so 2 chunks away from the player).When the rule is evaluated and a zombie tries to spawn, it checks if the closest player has the 'maxlocal' condition fulfilled. Since neither player has reached the cap within the chunks around them, the zombies are allowed to spawn. This happens even in the shared chunks and as only one of the players is checked and the probablity of a zombie spawning in any chunk around the player is uniform, this means that the chunks in range of both players will see double the spawns. This increases with the amount of players sharing chunks and can get worse if they are moving. It's possible for a player that has already reached their cap to get close to another player that has not filled their cap yet, when new spawns happen in the shared chunks, they overflow the mob cap from the first player. With 'perlocal' we can avoid all of this.