Solved Realistic custom mob spawning in region

Discussion in 'Spigot Plugin Development' started by MrDienns, May 8, 2017.

  1. Hi Spiglets

    I'm creating a custom mob plugin and I wish to know how I can best create realistic mob spawning within a specific (WorldGuard) region.

    * Realistic: Mobs not spawning on top or inside (or simply too close) to players and not overly populated (way too many monsters when nobody kills them). Also, if possible, I'd like monsters to stay in that region as much as possible (unless they are attacking and following a player).

    What would be the best way for me to do this?

  2. Listener for EntitySpawnEvent and if the entity is in region replace it for custom (NMS?) entity
    • Agree Agree x 1
  3. Well, no. I prefer to disable any natural monster spawning and have my custom one do all the work.
  4. Im not sure but i think that the custom (NMS) entities cant spawn naturally

    So you will need to replace the entity on the spawn Event
    or just use Timers to spawn on certain locations within certain conditions
  5. Obviously not no, that's the entire reason for this thread :D
  6. Run a timer for every 1 minute. Do a chance maybe like 25% . So every one minute. If the chance is met, Find a random location inside the region (by adding/substracting a random x/y lesser than the length/width from a point in the region) then getting the heightest point in the region, then summon the mob.

    Hope this helps.
  7. Why aren't you able to use CreatureSpawnEvent? If its a mob you are replacing, simply cancel it from spawning. Unless, of course, if it's inside the region you want it to spawn then allow it to spawn. Simple.

    For instance, if you are replacing all zombies with super zombies, then here's a pseudocode example:
    Code (Text):
    public void onZombieSpawn(CreatureSpawnEvent event) {
        LivingEntity entity = event.getEntity();
        if(entity.getType() != EntityType.ZOMBIE) {
            return; // not a zombie. allow.

        event.setCancelled(true); // cancel the zombie from spawning regardless of below outcome

        Region region = WorldGuard.getRegion(entity.getLocation());
        if (region == null || !region.getName().equals("MySpecialRegion")) {
            return; // this is not my special region.

        new SuperZombie().spawn(entity.getLocation()); // spawn the super zombie
    #7 BillyGalbreath, May 8, 2017
    Last edited: May 8, 2017
    • Like Like x 2
    • Agree Agree x 1
  8. ^^^
    .... he wants to completely disable normal mob spawning
  9. And thats exactly what my example above shows...
    • Agree Agree x 1
  10. You cancel CreatureSpawnEvent and getLocation/type and things and spawn a custom one (As told in my first post and repeated by @BillyGalbreath )
    • Agree Agree x 1
  11. Indeed. I felt the need to reiterate what you said because I dont think it was fully understood that thats how you "disable any natural monster spawning" ^_^

    I mean, sure. Theres an NMS way to do it. But its unreliable and most of the time only works when chunks first generate. The easiest, and sure fire way to do it is with the CreatureSpawnEvent. Its forwards and backwards compatible, and you dont have to worry about NMS issues when registering the entity to the biome tables.

    (for the record, my solution mentioned in the above link was to use CreatureSpawnEvent to replace random zombies with giants)
    #11 BillyGalbreath, May 8, 2017
    Last edited: May 8, 2017
    • Like Like x 1
  12. You editted that example in 1 minute after my post :/
  13. Took longer than a minute to write :p
  14. This seems fairly usefull. Thank you for this example.
    Let's say I want to do it even "better", and not use the CreatureSpawnEvent. Would you be able to come up with something creative? Like selecting a region, picking some random locations in it every 10 seconds, checking if its not too close to a player, and so on...

    Also, how would I prevent monsters from walking outside of the region (without attacking anyone)?
  15. I'm sure you could setup some elaborate repeating tasks and calculate your own distance checks, etc. But why would you want to do that when natural spawning already does all the distance checks for you?
    Since there is no EntityMoveEvent (it was decided not to create this event because it would be extremely laggy on servers) you will need to setup a repeating task to check the entity's location. What you do when it walks outside the region is up to you. You could despawn it, turn it around 180 degrees so it walks to the other way (this is a custom mob you wrote, so you're already one step closer to this), or maybe teleport it back a block or two inside the region (kinda like the enter/exit flags work for players). It's all really up to you and what you want.
    • Like Like x 2
    • Informative Informative x 1
  16. Yeah, I suppose it would save me alot of time just using the natural version. I'll be using alot of different monster types (including the 1.11 ones) and I'm still a little unsure about how they spawn, but this will surely send me in the right direction. Thanks!
  17. I've done this in a couple different ways. If your world is confined to a smallish area and you don't need mobs everywhere IMO it makes sense to use a timer and disable the natural mob spawning. If your players can roam anywhere in the world it makes more sense to modify the way they naturally spawn.

    Method #1 overriding natural mob spawning:
    This can be done in a couple of ways, it sounds like you have a fair amount of custom entities.
    1) allow the mob types that you have custom types for to spawn naturally in the world.
    2) Listen to the EntitySpawnEvent
    3) whenever event.getEntity() is an instanceof Zombie (or whatever custom type you have)
    4) cancel the event
    5) using event.getLocation() you know where this spawn *should* have occurred.
    6) create a method to spawn your NMS entity that is a different type of Zombie.
    *this will fire a second EntitySpawnEvent so it is critical that you spawn a type that is not an instanceof
    a type that is overriden. Otherwise you'll end up with an infinite loop of doom.

    The above method makes no effort to check for a region or area but will produce natural looking mob spawns all over the world using the vanilla rules, if a mob could spawn in that area it will, only it will be your custom type.

    Method 2: Region based mob spawning
    Another server we have players are in a more controlled world, they should never be outside of a worldguard region, as it automatically creates these regions as world sections are spawned.

    1) Set yourself up a repeating timer than runs every 50 ticks or longer if you prefer, mine does not spawn a mob 100% of the time so this setting works well for me.
    2) I have different mobs that spawn at night and day, as well as passive and hostile monsters, these are set up in a simple EntityType[] array. If i want a new mob to spawn I simply add it to the array.
    3) The timer first checks for players online, if there are none online spawning ceases.
    4) I have a master list of all regions, this list is iterated over and a timestamp is checked, this timestamp reflects the last time a player entered the region. **Note I needed to modify this to also flag the adjacent North/South/West/East regions from the player active as well to make sure players running through the area would encounter monsters.
    5) if a player was in the region recently spawning should probably occur. So the time is checked to see if it is day or night.
    6) Once the time of day is found it picks out a random "safe" location to spawn the monster in the region. Our "sections" are actual worldguard regions, and the world is mostly flat, however there are walls around, so the code checks the "floor" of the region to make sure that it isn't above a specific Y value which would mean they are on top of a wall, and that they wont spawn inside a block. Thus we are left with a list of open spots that can be randomly picked from. The code also ensures mobs wont spawn super close to the players who are in the region with a simple distance check.
    7) once we have a valid spawn location, a mob type is selected at random from either dayMobs, nightMobs, or passiveMobs list(s)? and that type of creature is spawned at the location.
    8) the mob is added to a list of spawned mobs that is maintained PER AREA so that it can be checked up on later, mobs should be removed from this list if they die at the hands of a player, or otherwise detectable means, or if the area is inactive for a few minutes with no player activity all mobs are removed.

    8) there is some cleanup code that runs on a larger delay inside this timer that goes through the list of spawned creatures, here I check to see if the monster has wandered out of the area by iterating over the active area's list of creatures, then checking to see if they are inside the region, if so and the creature is alive etc, skip them. If not they despawn (provided they aren't wearing a players gear) and are removed from the list.
    9) also as mentioned in #8 you should probably have some sort of activity timer to ensure their removal from the world as well as from the lists of active monsters if no players are around.
    10) finally by keeping a per-area list you are able to limit the number of spawned creatures within an area, on our server at night this number is increased to make night time ventures harder.

    If you would like more info on how I accomplished any of those above things let me know and I can provide more detail!
    • Like Like x 2
    • Winner Winner x 1