Jump to content

[Solved] Entities moved in same loop don't update at the same time.


don_bruce

Recommended Posts

For the past month I've been working on an airplane mod that uses multiple entities for different components.  Each entity represents a specific part of the plane, like wheels, propellers, or fuselage points.  To tie all the entities together, I link each to a parent, which moves all its children each tick.  I've got all the children moving just fine, the problem is that children spawned AFTER the main parent class move a split second BEFORE the parent does.  This means that any entities spawned within an extended parent constructor move with the parent, but any entities added after the parent is spawned move too early.  This is particularly troublesome when loading a saved world, as each entity gets loaded at different times and can cause an unspecified number of out-of-sync children.

 

Currently the parent's onUpdate method runs on the server and client to allow for fluid motion, with syncing packets being sent every 50 ticks.  I changed the onUpdate method to only run server-side, and then having sync packets sent every tick, which caused the stuttering to disappear, suggesting that this is a client-side issue.  Below is the routine where the children are moved, and the constructor for the parent entity.  The constructor is called by right-clicking an item, which simply executes world.spawnEntityInWorld() with a new parent entity provided.

 

Child Movement Section

 

public void moveChildren(){
	Iterator<EntityChild> childIterator = children.values().iterator();
	while(childIterator.hasNext()){
		EntityChild child = childIterator.next();
		if(child == null){
			childIterator.remove();
			--numberChildren;
		}else if(child.isDead){
			childIterator.remove();
			--numberChildren;
		}else{
			Vec3 offset = RotationHelper.getRotatedPoint(child.offsetX, child.offsetY, child.offsetZ, rotationPitch, rotationYaw, rotationRoll);
			child.setPosition(posX + offset.xCoord, posY + offset.yCoord, posZ + offset.zCoord);
			child.updateRiderPosition();
		}
	}
}

 

 

Item Code

 

public boolean onItemUse(ItemStack item, EntityPlayer player, World world, int x, int y, int z, int p_77648_7_, float hitX, float hitY, float hitZ){
	if(!world.isRemote){
		world.spawnEntityInWorld(new EntityPlaneMC172(world, x, y+2.25F, z, player.rotationYaw, item.getItemDamage()));
		return true;
	}else{
		return false;
	}
}

 

 

Parent Constructor

 

public EntityPlaneMC172(World world, float posX, float posY, float posZ, float rotation, int textureCode){
	super(world, posX, posY, posZ, rotation, (byte) 2, textureCode);
	world.spawnEntityInWorld(new EntityCore(world, this, this.UUID, 0, -0.3F, 1, 0));
	world.spawnEntityInWorld(new EntityCore(world, this, this.UUID, 0, -0.3F, -4.25F, 0));		
}

 

 

Spawning a plane with this setup results in the two EntityCore entities following the parent perfectly; however any entity spawned afterwards skips ahead.  Oddly enough, editing the item's code to create a new plane, but holding off spawning it until after the cores have spawned results in the cores not following the parent correctly either.  This suggests that there's something that Minecraft is doing after spawning the parent entity that's causing all the linked children to get out of whack.  I really don't see what it could be,  though, as all the child entities are moved in the same loop of code.

 

As a visual reference, here's two links to a couple of gifs that demonstrate what I'm talking about (note that jumps happen every tick; the gifs don't have enough FPS to display that).  The first gif is with the default item-constructor, while the second gif is the modified item-constructor.  In both gifs my character is riding a child, so any entity that's not wiggling is actually updating early.

 

Gif#1: http://i.imgur.com/v5Y8sWx.gifv

Gif#2: http://i.imgur.com/8F9fbUH.gifv

 

Hopefully somebody knows what's going on, cause I sure don't!

Link to comment
Share on other sites

I honestly didn't go through all your code. But a couple things come to mind. First of all, why are you sending your own packets to sync positions? Why can't you rely on the built-in movement syncing? If you're sending your own packets, I could understand where they might conflict with the packets the server is already sending.

 

Are you sending packets for all the parts, or only the parent? Because maybe you already did it this way, but instead of having the children try to catch up with the parent, you should have the parent re-position the children. If such code runs on the client I would be very surprised if they would get out of sync. Like in all cases in modding, it is best to examine how a similar vanilla mechanism works. So if you look at EntityDragon, you'll see that the EntityDragonPart class is really quite dumb and all the movement of the parts is done in the parent EntityDragon's onLivingUpdate() method, with the following code:

 

                this.dragonPartBody.onUpdate();

                this.dragonPartBody.setLocationAndAngles(this.posX + (double)(f16 * 0.5F), this.posY, this.posZ - (double)(f4 * 0.5F), 0.0F, 0.0F);

                this.dragonPartWing1.onUpdate();

                this.dragonPartWing1.setLocationAndAngles(this.posX + (double)(f4 * 4.5F), this.posY + 2.0D, this.posZ + (double)(f16 * 4.5F), 0.0F, 0.0F);

                this.dragonPartWing2.onUpdate();

                this.dragonPartWing2.setLocationAndAngles(this.posX - (double)(f4 * 4.5F), this.posY + 2.0D, this.posZ - (double)(f16 * 4.5F), 0.0F, 0.0F);

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Personally I think you should just render all the parts at once. By that I mean disable rendering for every entity except your plane base, and in your plane base just do some extra GL11 translations/rotations and then render the other pieces in their local coordinates with respect to the plane base. I hope that made sense  :P

 

Really though, assuming your entities positions were set properly, the linear interpolation of positions between ticks won't be accurate when the plane is rotating, and you'll still see stuttering. Just put the render code from the other parts into the renderBase and you'll be good.

"you seem to be THE best modder I've seen imo."

~spynathan

 

ლ(́◉◞౪◟◉‵ლ

Link to comment
Share on other sites

Normally I'd rely on the built-in tracker method; however the tracker is being troublesome in that it won't sync correctly.  I posted this problem earlier on the Minecraft Forums page here, which I believe you were helping me with, jabelar.  The end result was that I tracked down the method the tracker uses and overrode it to prevent the tracker from setting positions.  I thought that that might be the issue, but re-enabling that method doesn't fix the problem.

 

As to my packets: I only send packets to the parent entity.  Like the dragon, my children are dumb and don't move themselves.  They don't even get velocity information, as the movement is done using setPosition().  The only place I move a child is in the moveChildren() section of the parent, which happens after the parent calls moveEntity().  This should ensure that all the children are moved at the same time, right?

 

As to rendering them within the same call as the plane, that strikes me as a bit of a kludge.  Either way, I need an actual entity present to do collision detection, and some of the entities do special things (e.g. propeller entity changes texture if different model is given and applies damage to mobs).  The linear interpolation is pretty spot-on, as I move the children every tick and apply a rotation matrix using pre-set offsets.  Assuming no entities get antsy in their updating, you can't even tell that the plane's made up of multiple entities.

Link to comment
Share on other sites

I think he probably is interested in more than the rendering. He probably wants the multiple collision boxes as well.

 

If he doesn't care about the collision boxes, then yes can probably handle this all as one complex model (with parts that are enabled as appropriate).

 

But if he cares about collision boxes, then he needs to do it like EntityDragon -- associate child parts and sync them all up in the parent's update.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

You're correct on that jabelar.  The whole reason I'm making separate entities is so I can implement multipart collision.  For example, how can a plane land realistically on three wheels if the bounding box is just for the entire plane?  It also allows me to be a bit more creative with rendering options, like making a wheel turn only if it hits the ground, and making it break off if the plane clips a house during takeoff.  Yes, I could compile all the code into one entity, but then the plane wouldn't handle like a real plane, which is the whole reason I'm making this mod.

Link to comment
Share on other sites

You're correct on that jabelar.  The whole reason I'm making separate entities is so I can implement multipart collision.  For example, how can a plane land realistically on three wheels if the bounding box is just for the entire plane?  It also allows me to be a bit more creative with rendering options, like making a wheel turn only if it hits the ground, and making it break off if the plane clips a house during takeoff.  Yes, I could compile all the code into one entity, but then the plane wouldn't handle like a real plane, which is the whole reason I'm making this mod.

 

Okay, well as mentioned before, when modding it is best to check out the vanilla source code for any similar logic in Minecraft. The EntityDragon seems to do something very similar to what you want to do, so I would check out its code.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

I've poked around in the dragon code before.  After studying it for a while, I've come to suspect that the dragon has the same issues I'm having.  This is evidenced by the fact that the dragon spawns a new set of children each time the constructor is called, and leaves the old set orphaned.  Since the children are spawned within a constructor, they don't get antsy like entities spawned post-constructor.  This method won't work for me, as I want to be able to add entities to the plane post-spawn (like adding a wheel to repair one that broke).  My updating methods are the same as the dragon's, though, as the dragon simply moves the parent and then each part in sequence.

 

To test my code some more, I changed the parent to only update its position every 20th tick.  I then added print statements showing positions after moving the parent, after moving a specific child, and in the specific child's onEntityUpdate() method.  Through this I was able to confirm that the parent DOES have its position set before any children do.  Each child also gets its position set directly after the parent sets its position, and doesn't change position after the parent does.

 

There was an odd trend I noticed, which was that any child who was spawned after the parent had its onEntityUpdate() triggered that tick, while any child who was spawned before the parent had to wait until the next tick.  I'm starting to suspect this is a rendering routine error, considering that the this problem only happens on the client and the positions are fine.  It'd make sense, as normally Minecraft doesn't have to worry about multipart entities and can just run through the loaded entity list for rendering updates.  Even the dragon doesn't have to worry too much, as the whole dragon is rendered by the parent, and so jittering children wouldn't be noticed unless someone was looking at the bounding boxes.  I'm hoping this isn't the case, though, as it'd mean I have to go and re-work most of the entity-linking code.

Link to comment
Share on other sites

I think it is possible that the dragon has some mismatch that isn't noticeable due to overlap and "organic" shape -- it is much harder to notice a flapping wing is lagging than a fixed airplane wing.

 

However, I don't understand your point about creating the child parts in the constructor. I suppose you're saying that the order of entity registration seems to be affecting which tick the update occurs in, although that still seems very odd to me.

 

Anyway, there is a difference between "constructing" and "registration". You can always go ahead and register all the possible parts at the same time, and then just spawn them (which would call the constructor for the part) when needed. Theoretically you could probably actually spawn them all too, but make them invisible until needed. So if it is really a matter of registration order or constructor order, I think you could still manage it.

 

Another approach is to do all the rendering from the parent (so it will look solidly connected) and have the other parts as invisible entities. The collision boxes of those entities would still be out of sync a bit, but perhaps it would be tolerable. So you could still get fairly good collision processing, while making sure that the visuals are perfect.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

You're correct in that registration order has nothing to to with the problem; I register all my entities in the common proxy at initialization.  What I'm encountering is that the spawning order of the children relative to the parent determines whether they update correctly.  Any child that is spawned after the parent has its onUpdate() method called after movement is applied by the parent, while any child spawned before the parent has its onUpdate() called before the parent moves all the children.  It's this calling scheme that leads me to believe that whatever's calling onUpdate() is also adjusting the rendered position of the children.  Since the movement hasn't been applied for some children at that point, they don't update their 'positions' until the next tick.

 

As to the dragon, this line in the EntityDragon(World) constructor is the one I'm talking about:

this.dragonPartArray = new EntityDragonPart[] {this.dragonPartHead = new EntityDragonPart(this, "head", 6.0F, 6.0F), this.dragonPartBody = new EntityDragonPart(this, "body", 8.0F, 8.0F), this.dragonPartTail1 = new EntityDragonPart(this, "tail", 4.0F, 4.0F), this.dragonPartTail2 = new EntityDragonPart(this, "tail", 4.0F, 4.0F), this.dragonPartTail3 = new EntityDragonPart(this, "tail", 4.0F, 4.0F), this.dragonPartWing1 = new EntityDragonPart(this, "wing", 4.0F, 4.0F), this.dragonPartWing2 = new EntityDragonPart(this, "wing", 4.0F, 4.0F)};

That seems to indicate to me that whenever the dragon's class is loaded, either from spawning or from disk, new child entities are created.  Interestingly, those children don't seem to ever get spawned in the world, as only EntityDragon gets sent into a spawnEntityInWorld() call.  Does this mean I shouldn't be spawning my children?

Link to comment
Share on other sites

Never thought of using selective collision.  That would take care of the spawning issue, though saving the data would be a bit of a problem as you say.  Bouncing the ideas off of you got me thinking that the true issue was that children moved on the client at different points in the game tick.  I was already using a tick handler to check to see if the player was in a plane, so I hooked into that to move children on the client.  This works just fine, as children on the server update after the parent moves, while the children on the client move all together at the start of each tick.  Thanks for all your help; I'm marking this as solved.

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.

Announcements



×
×
  • Create New...

Important Information

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