Entity type
The organisms are roughly divided into four species: aggressive, passive, aquatic (i.e. squid) and environmental (aka bats). Attack mobs have a build cycle that ticks each game (1/20 seconds). Passive and aquatic organisms only generate cycles every 400 ticks (20 seconds). Because of this, attack mobs can spawn at any time, and animals spawn very little. In addition, most animals spawn in chunks generated when the world is built. The corresponding class in the source code is
Enumcreaturetype.java
public enum enumcreaturetype{MONSTER (Imob.class, Material.air, False, False), creature (Entityanimal.class, 10, Material.air, True, True), AMBIENT (Entityambientcreature.class, Material.air, True, false), Water_creature (entit Ywatermob.class, 5, Material.water, True, false); /** * The root class of creatures associated with this enumcreaturetype (imobs for aggressive creatures, entityanimals * for friendly ones) */private final Class Creatureclass; private final int maxnumberofcreature; Private final Material creaturematerial; /** A flag indicating whether this creature type is peaceful. */Private Final Boolean ispeacefulcreature; /** Whether This creature type was an animal. */Private Final Boolean isanimal; Private Enumcreaturetype (class class, int _maxnumberofcreature, Material _creaturematerial, Boolean _ Ispeacefulcreature, Boolean _isanimal) {this.creatureclass = class; This.maxnumberofcreature = _maxnumbErofcreature; This.creaturematerial = _creaturematerial; This.ispeacefulcreature = _ispeacefulcreature; This.isanimal = _isanimal; } public Class Getcreatureclass () {return this.creatureclass; } public int getmaxnumberofcreature () {return this.maxnumberofcreature; }/** * Gets Whether or not this creature type is peaceful. */Public Boolean getpeacefulcreature () {return this.ispeacefulcreature; }/** * Return Whether this creature type was an animal. */Public Boolean getanimal () {return this.isanimal; }}
The monster capacity (which can be understood as the population) is directly proportional to the total number of blocks that are suitable for generation. To calculate the capacity, the spawning area expands one chunk in each direction, so there is the size of the 17*17 chunk, and then the total number of chunks is replaced into the following:
Capacity = constant * Number of blocks/289
Each organism has its own capacity calculation and different constant values in the formula:
Attack type = 70
Passive type = 10
Environment type (BAT) = 15
Aquatic type = 5
Server architecture
Figure 1. MC Server Architecture
Minecraftserver as a server, mainly responsible for the service side of the update, which can contain multiple worldserver,worldclent as a service side, when players join a server, will create a local. Spawneranimals is used as a tool for brush monsters, mainly to handle the brush logic.
First look at Worldserver's tick.
Note: If not specifically stated, all ticks are 20 times a second.
Figure 2. Worldserver Tick Flow
Very clear logic, here is the main look at the implementation of findchunksforspawning.
In single player mode, the chunk count is always 17*17=289, so the capacity of each creature is the value listed above. In multiplayer, chunks within multiple players are counted only once, so the more dispersed the player, the more chunks will be overwritten and will have higher biomass capacity.
The capacity is checked at the beginning of each build cycle. If the number of surviving organisms exceeds its capacity, the entire generation cycle is skipped.
In each build cycle, an attempt is made to generate a set of mobs in each of the appropriate chunks. Select a random location within the block as the central point of this set of mobs. To generate this set of mobs, the central block must be a water block for aquatic organisms and must be an air block for all other mobs. Note that in the latter case, it must be an air block. Any other block, even a transparent block, will prevent the entire set of mobs from spawning.
Figure 3. Spawning conditions for land monsters
If the group is positioned properly, it will make 12 attempts to generate up to 4 mobs (Wolves are 8, ghasts are 1) in the range of the 41*1*41 (that is, the square size of the 41*41 lattice, which has a height of 1 blocks), with the center square as the origin. Mobs will spawn the bottom part of their body in this area. In each build attempt, the location of a block is randomly selected in this area. Although the spawning area can extend beyond the center 21, the randomly selected locations are strongly concentrated in the center of the group. Approximately 85% of the build will be within 5 grid of the group's center, and 99% will fall within 10 grid.
All creatures in the group are of the same species. Randomly pick one of the types that the region is fit to generate in order to determine the type of the whole group when the group first builds.
You can refer to the wiki for Minecraft in the specific category.
The Findchunksforspawning function implements the logic described above. Take a look at the Spawneranimals.java class.
Public final class spawneranimals{private static final int mob_count_div = (int) Math.pow (17.0D, 2.0D); /** the 17x17 area around the player where mobs can spawn */private final Set eligiblechunksforspawning = Sets.newhash Set (); private static final String __obfid = "cl_00000152"; /** * Adds all chunks within the spawn radius of the players to eligiblechunksforspawning. Pars:the World, * hostilecreatures, Passivecreatures. Returns number of eligible chunks. */public int findchunksforspawning (Worldserver server, Boolean spawnhostilemobs, Boolean spawnpeacefulmobs, Boolean Isspecialspawntick) {if (!spawnhostilemobs &&!spawnpeacefulmobs) {return 0; } else {this.eligibleChunksForSpawning.clear (); int chunkcount = 0; Iterator Iterator = Server.playerEntities.iterator (); int k; int creaturecount; while (Iterator.hasnext ()) {Entityplayer Entityplayer = (entityplayer) iterator.next (); if (!entityplayer.isspectator ()) {int J = mathhelper.floor_double (entityplayer.posx/1 6.0D); K = mathhelper.floor_double (entityplayer.posz/16.0d); byte B0 = 8; for (int l =-b0, l <= b0; ++l) {for (Creaturecount =-b0; Creaturecount < ; = B0; ++creaturecount) {Boolean flag3 = L = =-b0 | | l = = B0 | | creaturecount = =-b0 | | Creaturecount = = B0; Chunkcoordintpair Chunkcoordintpair = new Chunkcoordintpair (L + j, Creaturecount + K); if (!this.eligiblechunksforspawning.contains (Chunkcoordintpair)) { ++chunkcount; if (!flag3 && server.getworldborder (). ContaINS (Chunkcoordintpair)) {this.eligiblechunksforspawning . Add (Chunkcoordintpair); }}}}} int Totalentitycount = 0; Blockpos Blockpos2 = Server.getspawnpoint (); enumcreaturetype[] Aenumcreaturetype = Enumcreaturetype.values (); K = Aenumcreaturetype.length; for (int i = 0; i < K; ++i) {Enumcreaturetype enumcreaturetype = aenumcreaturetype[i]; if ((!enumcreaturetype.getpeacefulcreature () | | spawnpeacefulmobs) && (Enumcreaturetype.getpeacefulcrea Ture () | | Spawnhostilemobs) && (!enumcreaturetype.getanimal () | | isspecialspawntick)) {CR Eaturecount = Server.countentities (Enumcreaturetype, true); int maxcreaturecount = Enumcreaturetype. Getmaxnumberofcreature () * CHUNKCOUNT/MOB_COUNT_DIV; if (Creaturecount <= maxcreaturecount) {Iterator Iterator1 = This.eligiblech Unksforspawning.iterator (); arraylist<chunkcoordintpair> tmp = new ArrayList (eligiblechunksforspawning); Collections.shuffle (TMP); Iterator1 = Tmp.iterator (); Label115:while (Iterator1.hasnext ()) {Chunkcoor Dintpair Chunkcoordintpair1 = (chunkcoordintpair) iterator1.next (); Blockpos Blockpos = getrandomchunkposition (server, Chunkcoordintpair1.chunkxpos, Chunkcoordintpair1.chunkzpos); int j1 = Blockpos.getx (); int k1 = blockpos.gety (); int L1 = Blockpos.getz (); Block block = Server.getblockstaTe (Blockpos). Getblock (); if (!block.isnormalcube ()) {int entitycountonchunk = 0; int j2 = 0; while (J2 < 3) {int K2 = J1; int L2 = K1; int i3 = L1; BYTE B1 = 6; Biomegenbase.spawnlistentry spawnlistentry = null; Ientitylivingdata ientitylivingdata = null; int J3 = 0; while (true) {if (J3 < 4) {label108: { K2 + = Server.rand.nextInt (B1)-Server.rand.nextInt (B1); L2 + = server.rand.nextInt (1)-server.rand.nextInt (1); i3 + = Server.rand.nextInt (B1)-Server.rand.nextInt (B1); Blockpos blockpos1 = new Blockpos (K2, L2, i3); float f = (float) k2 + 0.5F; Float F1 = (float) i3 + 0.5F; Check must is away from the player by the block, and away from the player spawn point. 576 = if (!server. Checkcanspawnhere (Double) F, (double) L2, (double) F1, 24.0D) && Blockpos2.distancesq (double) F, (double) L2, ( Double) F1) >= 576.0D) { if (spawnlistentry = = null) {spawnlistentry = Server. Getspawnlistentry (Enumcreaturetype, BLOCKPOS1); if (spawnlistentry = = null) { Break label108; }} if (server. Checkchunkhasspawnentry (Enumcreaturetype, Spawnlistentry, BLOCKPOS1) && cancreaturetypespawnatlocation ( Entityspawnplacementregistry.getspawnpointtype (Spawnlistentry.entityclass), server, BLOCKPOS1)) {entityliving entityliving; Try {entityliving = (entityliving) spawnlisten Try.entityClass.getConstructor (new class[] {world.class}). newinstance (new object[] {server}); } catch (Exception Exception) {Exception.print StackTrace (); return totalentitycount; } entityliving.setlocationandangles (Double) F, (double) L2, (double) F1, Server.rand.nextFloat () * 360.0F, 0.0F); Result canspawn = Forgeeventfactory.canentityspawn (entityliving, server, F, L2, F1); if (Canspawn = = Result.allow | | (Canspawn = = Result.default && (entityliving.getcanspawnhere () && entityliving.handlelavamovement ()) )) {if (! Forgeeventfactory.dospecialspawn (entityliving, server, F1, L2, F1)) Ientitylivingdata = Entityliving.getentitydata (server.getdifficultyforlocation (New Blockpos (entityliving)), Ientitylivingdata); if (Entityliving.handlelavamovement ()) { ++entitycountonchunk; Server.spawnentityinworld (entityliving); } if(Entitycountonchunk >= forgeeventfactory.getmaxspawnpacksize (entityliving)) {Continue label115; } } Totalentitycount + = Entitycountonchunk; }} ++j3; Continue }} ++j2; Break } } } } } } } return totalentitycount; }} protected static Blockpos getrandomchunkposition (World Worldin, int x, int. z) {Chunk Chunk = World In.getchunkfromchunkcoords (x, z); int k = x * + worldIn.rand.nextInt (16); int L = z * + worldIn.rand.nextInt (16); int creaturecount = mathhelper.ceiling (Chunk.getheight (New Blockpos (k, 0, L)) + 1, 16); int J1 = worldIn.rand.nextInt (Creaturecount > 0? creatureCount:chunk.getTopFilledSegment () + 16-1); return new Blockpos (K, J1, L); } public static Boolean cancreaturetypespawnatlocation (Entityliving.spawnplacementtype placetype, World Worldin, Block Pos POS) {if (!worldin.getworldborder (). Contains (POS)) {return false; } else {block block = Worldin.getblockstate (POS). Getblock (); if (Placetype = = EntityLiving.SpawnPlacementType.IN_WATER) {return block.getmaterial (). Isliquid ()&& worldin.getblockstate (Pos.down ()). Getblock (). GetMaterial (). Isliquid () &&! Worldin.getblockstate (Pos.up ()). Getblock (). Isnormalcube (); } else {Blockpos blockpos1 = Pos.down (); if (!worldin.getblockstate (BLOCKPOS1). Getblock (). Cancreaturespawn (Worldin, BLOCKPOS1, Placetype)) { return false; } else {Block block1 = worldin.getblockstate (BLOCKPOS1). Getblock (); Boolean flag = block1! = Blocks.bedrock && Block1! = blocks.barrier; Return flag &&!block.isnormalcube () &&!block.getmaterial (). Isliquid () &&! Worldin.getblockstate (Pos.up ()). Getblock (). Isnormalcube (); }}}}/** * called during chunk generation to spawn initial creatures. */public static void Performworldgenspawning (World Worldin, BiomEgenbase biomegenbase, int chunkcenterx, int chunkcentery, int rangex, int rangey, Random rand) {List List = Bi Omegenbase.getspawnablelist (enumcreaturetype.creature); if (!list.isempty ()) {while (Rand.nextfloat () < Biomegenbase.getspawningchance ()) { Biomegenbase.spawnlistentry spawnlistentry = (biomegenbase.spawnlistentry) Weightedrandom.getrandomitem (WORLDIN.R and, list); int creaturecount = Spawnlistentry.mingroupcount + rand.nextint (1 + spawnlistentry.maxgroupcount- Spawnlistentry.mingroupcount); Ientitylivingdata ientitylivingdata = null; int J1 = Chunkcenterx + rand.nextint (Rangex); int k1 = chunkcentery + rand.nextint (rangey); int L1 = J1; int entitycountonchunk = K1; for (int j2 = 0; J2 < Creaturecount; ++j2) {Boolean flag = false; for (int k2 = 0;!flag&& K2 < 4; ++K2) {Blockpos Blockpos = Worldin.gettopsolidorliquidblock (New Blockpos (J1, 0, K1)); if (Cancreaturetypespawnatlocation (EntityLiving.SpawnPlacementType.ON_GROUND, Worldin, Blockpos)) { Entityliving entityliving; try {entityliving = (entityliving) spawnlistentry.entityClass.ge Tconstructor (new class[] {world.class}). newinstance (New object[] {Worldin}); } catch (Exception Exception) {EXCEP Tion.printstacktrace (); Continue } entityliving.setlocationandangles ((double) ((float) J1 + 0.5F), (double) Blockpos.gety (), (DOUBL e) ((float) k1 + 0.5F), rand.nextfloat () * 360.0F, 0.0F); Worldin.spawnentityinworld (entityliving); Ientitylivingdata = Entityliving.func_180482_a (worldin.getdifficultyforlocation (New BlockPos (entityliving)), Ientitylivingdata); Flag = true; } J1 + = Rand.nextint (5)-rand.nextint (5); for (k1 + = Rand.nextint (5)-rand.nextint (5); J1 < Chunkcenterx | | J1 >= chunkcenterx + rangex | | K1 < chunkcente RY | | K1 >= chunkcentery + rangex; K1 = Entitycountonchunk + rand.nextint (5)-Rand.nextint (5)) {J1 = L1 + Rand.nextint (5)-rand.nextint (5); } } } } } }}
The life cycle of monsters
Cond.
Special brush mechanism
Monster Spawner Logic
Cond.
Reference
Minecraft Wiki
Minecraft Forge
Minecraft Source code Analysis (1)-Spawn logic