Server/Client Projectile Pos Do Not Match
by Patrick Webber · in Torque 3D Professional · 07/09/2014 (3:04 pm) · 10 replies
Hello all,
I'm working on adding a bow and arrow into my game, and I'm trying to make it look as smooth as possible. To do this, I modified the projectile class so in my projectile datablock, I can flip an "isArrow" switch, and the projectile will expect to be taken through a number of steps before it allows for simulation to occur.
The arrow projectile expects to be mounted, then unmounted before it starts simulating the motion. I'm getting some strange results in multiplayer with this. The server position data and the client position data do not match while the arrow is mounted (which is while an animation is being played on the server). Below is a print out of the projectile's positions while the player is animating for the bow firing (while standing perfectly still).
It looks pretty good from each client's perspective, but the data just doesn't match up, and the result is a twitchy arrow that doesn't want to look right.
AnimateOnServer in the player class is set to true. And if I mount the arrow to another mount point and run around (projectile mounted to a hand):
I should also mention that the torso/legs are separate animation threads. ActionThread is legs, Thread1 is torso. I don't think this is affecting it.
One other thing about the bow animation is that its actually 2 animations playing when the bow is drawn. 1 is the arms moving to work the bow, the other is the back which tilts up and down when the player looks up/down.
So I don't think its the mount code? I'm guessing its something wonky with the server's animation or co-ordinates that I'm not accounting for? Any ideas on what's going on here?
Thanks,
Patrick
I'm working on adding a bow and arrow into my game, and I'm trying to make it look as smooth as possible. To do this, I modified the projectile class so in my projectile datablock, I can flip an "isArrow" switch, and the projectile will expect to be taken through a number of steps before it allows for simulation to occur.
The arrow projectile expects to be mounted, then unmounted before it starts simulating the motion. I'm getting some strange results in multiplayer with this. The server position data and the client position data do not match while the arrow is mounted (which is while an animation is being played on the server). Below is a print out of the projectile's positions while the player is animating for the bow firing (while standing perfectly still).
Client Position : -1.422110 1.530164 241.617325 Server position : 0.328504 1.096184 241.640259 Client Position : -1.376610 1.665740 242.027451 // delta x on client is +0.05 Server position : 0.217086 1.336059 242.025665 // delta x on server is -0.11 ??? Client Position : -1.190602 1.731347 242.380783 Server position : 0.134308 1.436136 242.358810 Client Position : -0.868336 1.720158 242.694122 Server position : 0.088053 1.446619 242.640335 Client Position : -0.582263 1.654325 242.881912
It looks pretty good from each client's perspective, but the data just doesn't match up, and the result is a twitchy arrow that doesn't want to look right.
AnimateOnServer in the player class is set to true. And if I mount the arrow to another mount point and run around (projectile mounted to a hand):
Server position : 2.709504 -4.739712 242.065994 Client Position : 2.709504 -4.739712 242.065994
I should also mention that the torso/legs are separate animation threads. ActionThread is legs, Thread1 is torso. I don't think this is affecting it.
One other thing about the bow animation is that its actually 2 animations playing when the bow is drawn. 1 is the arms moving to work the bow, the other is the back which tilts up and down when the player looks up/down.
So I don't think its the mount code? I'm guessing its something wonky with the server's animation or co-ordinates that I'm not accounting for? Any ideas on what's going on here?
Thanks,
Patrick
About the author
I'm a computer science major at Stony Brook University, and have been working with the Torque Game Engine for a number of years.
#2
I knew about the simulation stuff, and actually the client/server simulation doesn't actually occur with the data I've shown (it's still mounted, and simulation is turned off). I hadn't really looked at (the data of) the motion of the arrow, but I guess that it makes sense that a slow moving projectile would start to get interesting without interpolation code.
I never really considered creating a path for a fired arrow. I'm reading that as:
1. Client prepares to fire arrow (mounts it to the player for the animation), looking up and down/left and right as needed.
2. When client releases the arrow, the server calculates an arc path and places nodes along it.
3. The collision would be calculated each tick by the difference in transform on the server side.
Am I reading you right? I guess you wouldn't really use a projectile for this anymore though, probably just any networked shape object? It has been a while since I've touched paths, I'll have to take a look at those again.
07/10/2014 (10:21 am)
Thanks for the response Richard!I knew about the simulation stuff, and actually the client/server simulation doesn't actually occur with the data I've shown (it's still mounted, and simulation is turned off). I hadn't really looked at (the data of) the motion of the arrow, but I guess that it makes sense that a slow moving projectile would start to get interesting without interpolation code.
I never really considered creating a path for a fired arrow. I'm reading that as:
1. Client prepares to fire arrow (mounts it to the player for the animation), looking up and down/left and right as needed.
2. When client releases the arrow, the server calculates an arc path and places nodes along it.
3. The collision would be calculated each tick by the difference in transform on the server side.
Am I reading you right? I guess you wouldn't really use a projectile for this anymore though, probably just any networked shape object? It has been a while since I've touched paths, I'll have to take a look at those again.
#3
I suppose you could use any networked object, but if a projectile works why not use it? It already has all of the collision, damage, ammo/inventory stuff attached so I'd personally just try it first. Might save some effort, might not....
Wait - what about grenades? Could try using a "grenade" for the arrow....
07/10/2014 (11:55 am)
Sounds like what I was thinking - though the projectile should be able to tell when it hits something on its own.I suppose you could use any networked object, but if a projectile works why not use it? It already has all of the collision, damage, ammo/inventory stuff attached so I'd personally just try it first. Might save some effort, might not....
Wait - what about grenades? Could try using a "grenade" for the arrow....
#4
So for example if this is a server-initiated event you'd expect to see the server slightly ahead of the client, but the two should go through more or less the same positions with the client lagging behind slightly. Your numbers are inconsistent enough that I think there's something else wrong beyond the usual confusion of networking logic.
What I'd expect to see would be...
Server: Position 0
Client: Position 0
Server: Position 1
Client: Position 0
Server: Position 2
Client: Position 1
Server: Position 3
Client: Position 2
...Not perfectly identical positions, but it should be close. Anyway my point is that I think there's something wrong with the server animation.
Whether or not you can accomplish this using projectiles is another issue (by default projectiles don't update their positions or velocities to the client ghost after they're created), but I think this needs to be figured out first. I would track and output the actual position of the mount node in question instead of the projectile itself just to try to isolate the issue.
Just to paint a picture of how the projectiles are normally used: -Client sends trigger event in move packet
-Server processes tick and image goes to fire state
-Server script creates new projectile.
-Client receives fire state update from server, does animation and sounds, DOES NOT create a projectile.
-Generally during this same tick (but not always) the client receives a ghost of the projectile created on the server. By the time the client gets this ghost the projectile has already traveled some distance from the muzzle and so the first time we "see" the projectile on the client it's no longer at its origin point.
-No position/velocity updates are received for the projectile unless it bounces, in which case new position and trajectory data is received.
Obviously this model doesn't work for you since you want a smooth projectile launch, so you won't want it to appear slightly ahead of its actual "launch" point. And your firing animation involves a lot of arm movement that might be hard to sync up (but is actually doable since image states can trigger animations on the Player).
You might be able to do this if you disabled projectile ghosting (so the server projectile doesn't send an instance of itself to the client) and had clients actually create their own local projectiles only for visual effect. You'd still let the server determine hits, but the clients would see locally-created projectiles that should 99% of the time follow the same path (they'll just be slightly behind the server because they'll be created ~25ms later than the ones on the server).
07/12/2014 (2:10 pm)
Something's off, because even though the positions should be out of time sync (the entire client/server model is based on an understanding that the client and server instances of an object can never actually be in sync from a time perspective) you should be seeing very similar numbers, just at different points in time.So for example if this is a server-initiated event you'd expect to see the server slightly ahead of the client, but the two should go through more or less the same positions with the client lagging behind slightly. Your numbers are inconsistent enough that I think there's something else wrong beyond the usual confusion of networking logic.
What I'd expect to see would be...
Server: Position 0
Client: Position 0
Server: Position 1
Client: Position 0
Server: Position 2
Client: Position 1
Server: Position 3
Client: Position 2
...Not perfectly identical positions, but it should be close. Anyway my point is that I think there's something wrong with the server animation.
Whether or not you can accomplish this using projectiles is another issue (by default projectiles don't update their positions or velocities to the client ghost after they're created), but I think this needs to be figured out first. I would track and output the actual position of the mount node in question instead of the projectile itself just to try to isolate the issue.
Just to paint a picture of how the projectiles are normally used: -Client sends trigger event in move packet
-Server processes tick and image goes to fire state
-Server script creates new projectile.
-Client receives fire state update from server, does animation and sounds, DOES NOT create a projectile.
-Generally during this same tick (but not always) the client receives a ghost of the projectile created on the server. By the time the client gets this ghost the projectile has already traveled some distance from the muzzle and so the first time we "see" the projectile on the client it's no longer at its origin point.
-No position/velocity updates are received for the projectile unless it bounces, in which case new position and trajectory data is received.
Obviously this model doesn't work for you since you want a smooth projectile launch, so you won't want it to appear slightly ahead of its actual "launch" point. And your firing animation involves a lot of arm movement that might be hard to sync up (but is actually doable since image states can trigger animations on the Player).
You might be able to do this if you disabled projectile ghosting (so the server projectile doesn't send an instance of itself to the client) and had clients actually create their own local projectiles only for visual effect. You'd still let the server determine hits, but the clients would see locally-created projectiles that should 99% of the time follow the same path (they'll just be slightly behind the server because they'll be created ~25ms later than the ones on the server).
#5
07/12/2014 (2:15 pm)
You know, Henry, this might be why rockets still sometimes warp through things.
#6
I know of one case that's not really an error so much as an oversight... if you have a weapon that's particularly long and its muzzle point is outside the player collision box you can put the gun through a wall and then fire, resulting in the appearance of projectiles traveling through walls (they really didn't, they were created on the other side).
The first hacky solution I can think of for that one is to run a raycast at fire time from the weapon mount point (hand) to muzzle point to make sure there isn't an obstruction. If there is then ... handle that in some way that makes sense. Maybe launch the projectile from muzzlePoint - muzzleVector * 0.5 (half a meter back from the muzzle point along the muzzle vector).
07/12/2014 (2:38 pm)
I remember looking into that a couple times, I think it got pushed so far down on my list of issues that I eventually forgot about it. There was a thread Steve was heavily involved in on the topic, and I think there actually was a solution to one case found there.I know of one case that's not really an error so much as an oversight... if you have a weapon that's particularly long and its muzzle point is outside the player collision box you can put the gun through a wall and then fire, resulting in the appearance of projectiles traveling through walls (they really didn't, they were created on the other side).
The first hacky solution I can think of for that one is to run a raycast at fire time from the weapon mount point (hand) to muzzle point to make sure there isn't an obstruction. If there is then ... handle that in some way that makes sense. Maybe launch the projectile from muzzlePoint - muzzleVector * 0.5 (half a meter back from the muzzle point along the muzzle vector).
#7
07/12/2014 (4:17 pm)
The case I'm thinking of is when you stand back 9~15 units from an object (seems to be pretty closely tied to latency and once I find the range I can reproduce quite easily) rockets will simply pass through the object. Closer or farther than the "sweet spot" work fine. Only seems to happen when the object is not a static mesh object (TSStatic, StaticShape, etc) - my case more precisely is shooting at a RigidShape object, and I've reproduced it reliably in T3D commercial 1.1 and 1.2, and in MIT 3.0 on a lark (it's getting harder to get those scripts to work at all as T3D evolves - just a lot of little differences).
#8
07/12/2014 (4:59 pm)
I hate to keep hijacking this question thread, but that does sound like it's worth investigating. Could you give me some details about the setup so I can try to reproduce it? The muzzle velocity of the projectile and the type of collision hull on the object are all I can think of at the moment. I'm assuming the RigidShape is resting (not necessarily "atRest" but at least not moving). I've fired quite a few medium velocity projectiles at Vehicle objects, but usually I'm working with lots of hitboxes so even if it misses something it would probably hit the next box in line. I should be able to use the fake lag functions to emulate some different latency situations.
#9
The target is the "deathball" from the Deathball Desert mission.
Datablock:
07/12/2014 (6:05 pm)
The projectile is the default rocket.The target is the "deathball" from the Deathball Desert mission.
Datablock:
datablock RigidShapeData( DeathBall )
{
category = "DeathBallShape";
//className = "deathball";
shapeFile = "art/shapes/deathball/ballodeath.dts";
emap = true;
// Rigid Body
mass = 50;
massCenter = "0 0 0"; // Center of mass for rigid body
massBox = "0 0 0"; // Size of box used for moment of inertia,
// if zero it defaults to object bounding box
drag = 0.2; // Drag coefficient
density = "4";
bodyFriction = 0.2;
bodyRestitution = 0.25;
minImpactSpeed = 5; // Impacts over this invoke the script callback
softImpactSpeed = 5; // Play SoftImpact Sound
hardImpactSpeed = 15; // Play HardImpact Sound
integration = 4; // Physics integration: TickSec/integration
collisionTol = 0.25; // Collision distance tolerance
contactTol = 0.3; // Contact velocity tolerance
minRollSpeed = 1;
maxDrag = 0.25;
minDrag = 0.01;
triggerDustHeight = 1;
dustHeight = 10;
dragForce = 0.05;
vertFactor = 0.05;
normalForce = 0.05;
restorativeForce = 0.05;
rollForce = 0.05;
pitchForce = 0.05;
isInvincible = true;
};Ball loading:singleton TSShapeConstructor(BallodeathDts)
{
baseShape = "./ballodeath.dts";
};
function BallodeathDts::onLoad(%this)
{
%this.addNode("Col-1", "", "0 0 0 0 0 1 0", "0");
%this.addNode("ColSphere-1", "Col-1", "8.34465e-007 3.21e-007 0 1 0 0 0", "0");
%this.addCollisionDetail("-1", "Sphere", "mesh_ball", "4", "30", "30", "32", "30", "30", "30");
}This may not work in 3.5.1 - haven't tried it yet. Collision code shouldn't have drifted much.... This works in 1.1 and 1.2.
#10
@Henry
I was actually expecting to see what you posted above as well. I originally felt like animations on the server were being interpreted wrong somewhere... You have any idea as to what might be going on there or somewhere to look for a problem? Also, is there an easy way to disable only projectile ghosting? Can't say I'm aware of one off hand...
07/12/2014 (8:47 pm)
I can't say I've ever noticed this stuff with projectiles before, most of my projects were either melee combat or no combat at all!@Henry
I was actually expecting to see what you posted above as well. I originally felt like animations on the server were being interpreted wrong somewhere... You have any idea as to what might be going on there or somewhere to look for a problem? Also, is there an easy way to disable only projectile ghosting? Can't say I'm aware of one off hand...
Torque Owner Richard Ranft
Roostertail Games
Maybe for a slow projectile like an arrow it would be better to create a path when the arrow is fired and have the arrow follow it, then destroy the path when the arrow hits something.