Jump to content

mobs spawned with spawnEntityInWorld disappear.


esgeroth

Recommended Posts

I am trying to spawn a custom mob I made using spawnEntityInWorld. To do so I am using the LivingSpawnEvent.CheckSpawn event to detect when a certain mob is spawned and then replace it with my own. My mob is spawned but doesn't do anything and then disappears after a few seconds. The mob works fine when spawned with an egg. Just to make sure that my mob is not the problem I have tried spawning vanilla mobs instead with the same result.

 

Here is the code I am using.

 

@SubscribeEvent

public void onMobSpawnEvent( LivingSpawnEvent.CheckSpawn event )

{

  if(  EntityList.getEntityString( event.entity ) == "Spider" )

  {

    if( !event.world.isRemote )

    {

      EntityBat bat = new EntityBat( event.world );

      bat.posX = event.entity.posX;

      bat.posY = event.entity.posY;

      bat.posZ = event.entity.posZ;

 

      event.world.spawnEntityInWorld( bat );

      event.setResult( Result.DENY );

    }

  }

}

 

Using this a bat is spawned in place of a spider. But the bat does not move and despawns after a few seconds.

Link to comment
Share on other sites

Try too only check the worldObj.isRemote for the spawning. Leave the other code outside of the iff check.

No, that is completely wrong. If isRemote is true, that means it is a CLIENT world, and you DO NOT want to spawn an entity on the client - server only. Besides, the code that calls this event is only in WorldServer, so following your advice would result in nothing happening at all.

 

@OP That is very strange - I haven't used that particular event before, nor do I see anything in its placement that would suggest it shouldn't work perfectly fine, but I've had success doing something similar using the EntityJoinWorldEvent.

 

As an aside - rather than comparing strings, why not use an instanceof check, or Class#isAssignableFrom if you want to ensure ONLY that class (as in, not any sub-classes, such as if a mod extends EntitySpider):

if (event.entity instanceof EntitySpider) {
  // it's either a spider or any sub-class thereof; e.g. a cave spider will also pass this check
}

if (event.entity.getClass().isAssignableFrom(EntitySpider.class)) {
  // only the regular vanilla spider will pass this check, cave spiders will not
}

Link to comment
Share on other sites

Thanks, I found the problem. Apparently if I don't use setLocationAndAngles to assign rotation angles as well as position, the server must think that the entity is invalid and deletes it.

Also LivingSpawnEvent.CheckSpawn was not the right event to use as this is called constantly as the server searches for a valid spawn position. It worked but I ended up with a million bats on the screen. EntityJoinWorldEvent is fired only after a valid spawn point has been found and an entity is actually about to spawn there.

After fixing my original problem and adding in suggestions above, here is my working code to spawn a custom mob in place of a vanilla mob:

 

 

@SubscribeEvent

public void onMobSpawnEvent( EntityJoinWorldEvent event )

{

if(  event.entity instanceof EntitySpider && Math.random() < 0.3 )

{

event.setCanceled( true );

if( !event.world.isRemote )

{

EntityBigSpider bigSpider = new EntityBigSpider( event.world );

bigSpider.setLocationAndAngles(event.entity.posX, event.entity.posY, event.entity.posZ, 0.0F, 0.0F);

event.world.spawnEntityInWorld( bigSpider );

}

}

}

Link to comment
Share on other sites

if (event.entity.getClass().isAssignableFrom(EntitySpider.class)) {
  // only the regular vanilla spider will pass this check, cave spiders will not
}

 

This is not true. isAssignableFrom does the same check as instanceof. If you want to only check for vanilla spider use .getClass() == EntitySpider.class.

Diesieben, I'm shocked you don't know the difference, as they are definitely NOT the same. Put this in EntityInteractEvent and right click on some entities:

System.out.println("Entity class: " + event.target.getClass());
System.out.println("Instanceof EntityLiving      : " + (event.target instanceof EntityLiving ? "true" : "false"));
System.out.println("Assignable from EntityLiving : " + event.target.getClass().isAssignableFrom(EntityLiving.class));
System.out.println("EntityLiving assignable from : " + event.target.getClass() + ": "  + EntityLiving.class.isAssignableFrom(event.target.getClass()));

Here is the output you will see:

Entity class: class zeldaswordskills.entity.npc.EntityGoron
Instanceof EntityLiving      : true
Assignable from EntityLiving : false // This one right here is the big difference
EntityLiving assignable from : class zeldaswordskills.entity.npc.EntityGoron: true

#isAssignableFrom can appear to act like instanceof depending on which class you compare to which. If a more specific class is the caller and a less specific class is the method argument, you will get false, as you cannot create the more specific class (such as EntityCaveSpider) from a less specific one (such as EntitySpider).

 

In other words:

// okay to assign an EntitySpider from an EntityCaveSpider
EntitySpider spider = new EntityCaveSpider(world);

// NOT okay to assign EntityCaveSpider from EntitySpider
EntityCaveSpider spider = new EntitySpider(world);

Link to comment
Share on other sites

Uhm, you are just using it wrong, my friend. The arguments are inverted:

Sorry, but I disagree - there is nothing in the specification that requires it to be used with the calling class and argument in a specific order, especially given that #isAssignableFrom is usually used when there is not an object instance available or the object's type is not otherwise known until run-time.

 

Here is part of the description of #isAssignableFrom:

Specifically, this method tests whether the type represented by the specified Class parameter can be converted to the type represented by this Class object via [b]an identity conversion[/b] OR via a [url=http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.5][b]widening reference conversion[/b][/url]

It is the widening reference conversion which gives it the subtle behavior depending on the order of arguments:

A widening reference conversion exists from any reference type S to any reference type T, [b]provided S is a subtype (§4.10) of T[/b]. 

Therefore, if S is a subtype of T, and you then call T.class.isAssignableFrom(S.class) instead, you get false as expected; while instanceof would also give you false in such a case, it requires that you have an instance of T to test against S, which sometimes is impossible, and that's where #isAssignableFrom shines.

 

Anyway, I don't mean to start an argument here, I was just surprised by your statements, and your suggestion of testing class identity is a more suitable solution anyway ;)

 

EDIT: Ah, I see what you mean now - you are correct that my usage of it may be confusing to people, as #isAssignableFrom does not usually imply that the classes are equal (though it does in this case because all of EntitySpider's parent classes are abstract), and therefore is inappropriate to use in this case. Okay, after all that, I agree with you. Damn it :P

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.