Synchronizing physics

Hi

I want to do a multiplayer space game (not MMO like, more like a battlefield or wolfenstein - enemy territory, 32 players max). I’m new to physics in general in jme3 (was using jme2 without physics before).
This is my idea/understanding of how one can synchronize physics:

  1. The server runs the actual physics simulation (i.e. has the BulletAppState).
  2. Clients do not have any physics, they just get position and rotation updates and so on (not like in battlfield where clients also run collision detection)
  3. The player controls his ship via controls directly in the client - so the actual spatial is moved and rotated in the client (client side prediction would that be) - the position and rotation data is send via vector3f and matrix4f to the server.
  4. The server checks these values (so cheaters cannot teleport around) and applies them to the servers RigidBodyControl via setPhysicsLocation and setPhysicsRotation.

1 and 2 should be ok, but I don’t know yet about 3 and 4. Does this make sense? Can I just call setPhysicsLocation and setPhysicsRotation on the server side and be done with it?
Another way would be that the client only sends commands, like “move forward”, “rotate left” etc. But how could you ever translate this into RigidBodyControl-method calls? Moving forward/backwards and strafing left/right could be done with impulses or a velocity I guess, but rotating?

Hope you can help me out here :slight_smile:

@Wasserleiche said: Can I just call setPhysicsLocation and setPhysicsRotation on the server side and be done with it?

No. Look at this: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:physics
The chapter called “Kinematic vs Dynamic vs Static”
The physics engines burns many CPU cycles calculating where things end up if they obey the laws of physics. If you set the location hard it throws everything out the window and messes up all the other calculations the engine has done.

Another way would be that the client only sends commands, like "move forward", "rotate left" etc. But how could you ever translate this into RigidBodyControl-method calls? Moving forward/backwards and strafing left/right could be done with impulses or a velocity I guess, but rotating?

That is the only way to do it, mixing in calculations from the client and sending to the server will not work since you can’t trust them anyways.

How to rotate is explained on the same link in the section “Forces: Moving Dynamic Objects”

Ok thanks for your answer.
However I don’t quite get how rotating is supposed to work in my case: The controls are 3rd person. I’m attaching a CameraNode to my space ship and by holding down the right mouse button and moving the mouse around the player can steer the ship. What would you send to the server and what methods would you call on RigidBodyControl? I’m really confused here :confused:

Send quaternions + bindstates to server

Eg looking forward (quaternion or viewVector3f) and pressing right strafe(bindstates).
Everything else should be done by server.

Especially do not trust anything coming from the client, really.

Probably obligatory reading:
https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking
https://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/Server_In-game_Protocol_Design_and_Optimization

@Empire Phoenix said: Send quaternions + bindstates to server

Eg looking forward (quaternion or viewVector3f) and pressing right strafe(bindstates).
Everything else should be done by server.

Especially do not trust anything coming from the client, really.

This seems to contradict the statement of jmaasing - when I send a quaternion to the server, what method of RigidBodyControl should I call other than setPhysicsRotation (which he said was not a good idea)?
The rotation of the ship done by input of the player must be somehow translated into a force or an impulse - but how? Is this actually possible here?

@pspeed said: Probably obligatory reading: https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking https://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/Server_In-game_Protocol_Design_and_Optimization

Thanks for the links, but they don’t really answer my questions :confused:
I see that they also send a viewVector, but they don’t say how they apply it in their physics simulation.

It depends.

The quaternion/Viewdir is merely a rotation information.

-> For rigidbody you can calculate the necessary torque to reach the targeted rotation
-> For the BetterCharactrer controll you can apply it mostly directly (still you need to check it vor being a valid quaternion, else all kind of funny things might happen)

@Empire Phoenix said: For rigidbody you can calculate the necessary torque to reach the targeted rotation

How can you calculate the torque based on two quaternions?

I think I have thought about the hole thing wrong. I wanted only the server to calculate physics and set position and rotation data to the clients and clients should only send commands. But you obviously need client side prediction or else movement is just not smooth at all. So I initially thought that the local players ship at each client just moves when the actual player commands it (so it moves immediatelly in the client) and sends it data to the server, who distributes that to all clients, and in the localplayers client some comparing happens, since the server is the final authority. I guess you could do some movement predictions for the other players based on there current speed and direction.
But what about collisions? If only the server does collision detection, there are bound to be visual issues on the client since clients may get collision events from the server too late, so ships will move inside other ships in the clients for a while and then suddenly bounce back, which just looks wrong.
So shouldn’t I also do collision detection on the clients for client side prediction? The server will of course do the same to act as the final authority and clients will have to compare and interpolate their positions with the servers.
This would also make it easier to move and rotate my ships on input, because the same code will be used on server and client, since both have physics objects.
What do you think?

@Wasserleiche said: I think I have thought about the hole thing wrong. I wanted only the server to calculate physics and set position and rotation data to the clients and clients should only send commands. But you obviously need client side prediction or else movement is just not smooth at all. So I initially thought that the local players ship at each client just moves when the actual player commands it (so it moves immediatelly in the client) and sends it data to the server, who distributes that to all clients, and in the localplayers client some comparing happens, since the server is the final authority. I guess you could do some movement predictions for the other players based on there current speed and direction. But what about collisions? If only the server does collision detection, there are bound to be visual issues on the client since clients may get collision events from the server too late, so ships will move inside other ships in the clients for a while and then suddenly bounce back, which just looks wrong. So shouldn't I also do collision detection on the clients for client side prediction? The server will of course do the same to act as the final authority and clients will have to compare and interpolate their positions with the servers. This would also make it easier to move and rotate my ships on input, because the same code will be used on server and client, since both have physics objects. What do you think?

These issues are discussed at length in the articles:
https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking

The short answer, do all of the physics on the server. Let the client rotate if you want as it tends to ‘feel’ better. Render at a delay interpolating position based on server data that you’ve received. Prediction is pretty hard and probably not even necessary. Especially if your ships accelerate/decelerate, a 100 to 200 ms delay in rendering won’t really be noticed.

Put another way, 200 ms… if you were listening to a fast dance track at 140 bpm, 200 ms is less than half a beat. And your ship’s acceleration will cover it anyway.

I don’t think that client side prediction is not necessary - I think almost all games use this because else controls don’t feel responsive. I also read on many articles and forum posts that most games use client side collision detection. I think I’m going to try this.

But I still don’t know how to calculate a torque impulse from two quaternions and googling just does not seem to help very much. Anyone can help me out here?

@Wasserleiche said: I don't think that client side prediction is not necessary - I think almost all games use this because else controls don't feel responsive. I also read on many articles and forum posts that most games use client side collision detection. I think I'm going to try this.

Well, you can read the articles and then go play valve games. I get the feeling that you haven’t read them… for most people it takes two or three reads. (I took notes the second time.)

I’ve implemented networking without prediction and it feels ok. Unless your game has instant acceleration the delay is eaten in the delay of acceleration. And if you don’t, well then your game probably already feels weird.

I do minimal clieint-side prediction just to eat UDP delays and yes, you then do client-side collision detection… but not collision response. Detect the collision, stop the player, but don’t move the things they run into… the server is about to take care of it.

@Wasserleiche said: But I still don't know how to calculate a torque impulse from two quaternions and googling just does not seem to help very much. Anyone can help me out here?

I don’t understand why need to. Torque and quaternions are not even the same thing, really. Torque is an angular force and quaternions are a rotation. It’s like comparing vectors and matrices, it just doesn’t make sense without some additional information.

So glad that the site is back up again :smiley:

So it makes sense to at least detect collisions at client and stop them in the client and “wait” for the server to set them on their new path, ok.
I was playing around with controlling my ship with physics and did something stupid at first - manitpulating the linear and angular velocity directly. Collisions looks so damn weird, I had no idea XD. So instead I’m trying to only use impulses and torques, and I’m almost done with playing around with the numbers etc.
The only thing thats not working is performing a complete looping with my ship - at some point (I think it is when the loop would be complete) the rotation just stops. I can rotate a little back and then up again, but it always stops at the same position. Here is my code for rotating (inspired by the ShipPhysicsControl in the jme simple examples):

[java]
@Override
public void prePhysicsTick(PhysicsSpace arg0, float tpf) {
if (bodyControl != null) {
if (cameraMoveButtonPressed) {
Vector2f click2d = inputManager.getCursorPosition().clone();

			Vector3f click3d0 = camera.getWorldCoordinates(click2d, 0f);
			Vector3f click3d1 = camera.getWorldCoordinates(click2d, 1f);
			
			Vector3f viewDir = click3d1.subtract(click3d0).normalize();
			
			Quaternion originRot = bodyControl.getPhysicsRotation();
			Quaternion cloneRot = originRot.clone();
			Quaternion targetRot = cloneRot.clone();
			
			targetRot.lookAt(viewDir, Vector3f.UNIT_Y);
			cloneRot.slerp(targetRot, tpf);
			
			Vector3f dirSpatial = originRot.mult(Vector3f.UNIT_Z);
			Vector3f dirCam = cloneRot.mult(Vector3f.UNIT_Z);
			Vector3f cross = dirSpatial.cross(dirCam);
			
			Vector3f dirSpatial1 = originRot.mult(Vector3f.UNIT_Y);
			Vector3f dirCam1 = cloneRot.mult(Vector3f.UNIT_Y);
			Vector3f cross1 = dirSpatial1.cross(dirCam1);
			
			Vector3f dirSpatial2 = originRot.mult(Vector3f.UNIT_X);
			Vector3f dirCam2 = cloneRot.mult(Vector3f.UNIT_X);
			Vector3f cross2 = dirSpatial2.cross(dirCam2);
			
			Vector3f shipPosition = playerShip.getLocalTranslation();
			
			Vector3f originTargetPoint = shipPosition.add(originRot.getRotationColumn(2));
			Vector3f newTargetPoint = shipPosition.add(targetRot.mult(Vector3f.UNIT_Z));
			
			Vector3f force = newTargetPoint.subtract(originTargetPoint);
			
			bodyControl.applyTorque(bodyControl.getAngularVelocity().negate().multLocal(150f));
			float rotateSpeed = tpf * force.length() * 18000f;
			Vector3f torque = cross.addLocal(cross1).addLocal(cross2).normalizeLocal().multLocal(rotateSpeed);
			bodyControl.applyTorque(torque);
		} else {
			bodyControl.applyTorque(bodyControl.getAngularVelocity().negate().multLocal(20f));
		}
	}
}

[/java]

How can I make it possible to do a whole looping with my ship and not get stuck in mid rotation?

Like, you are trying to loop up… through your up vector?

Hm, turns out that this works much better:

[java]
@Override
public void prePhysicsTick(PhysicsSpace arg0, float tpf) {
if (bodyControl != null) {
if (cameraMoveButtonPressed) {
Vector2f click2d = inputManager.getCursorPosition().clone();

			Vector3f click3d0 = camera.getWorldCoordinates(click2d, 0f);
			Vector3f click3d1 = camera.getWorldCoordinates(click2d, 1f);
			
			Vector3f viewDir = click3d1.subtract(click3d0).normalize();
			
			Quaternion originRot = bodyControl.getPhysicsRotation();
			Quaternion cloneRot = originRot.clone();
			Quaternion targetRot = cloneRot.clone();
			
			targetRot.lookAt(viewDir, Vector3f.UNIT_Y);
			cloneRot.slerp(targetRot, tpf);
			
			Vector3f shipPosition = playerShip.getLocalTranslation();
			
			Vector3f originViewDirection = originRot.getRotationColumn(2);
			Vector3f originTargetPoint = shipPosition.add(originViewDirection);
			Vector3f newTargetPoint = shipPosition.add(targetRot.mult(Vector3f.UNIT_Z));
			
			Vector3f force = newTargetPoint.subtract(originTargetPoint);
			
			Vector3f torque = bodyControl.getAngularVelocity().negate().multLocal(150f);
			bodyControl.applyTorque(torque);
			
			clientState.sendMessage(new InputMessage(Vector3f.ZERO, torque));
			
			float rotateSpeed = tpf * force.length() * 30000f;
			torque = originViewDirection.cross(force).multLocal(rotateSpeed);
			bodyControl.applyTorque(torque);
			
			clientState.sendMessage(new InputMessage(Vector3f.ZERO, torque));
		} else {
			Vector3f torque = bodyControl.getAngularVelocity().negate().multLocal(20f);
			bodyControl.applyTorque(torque);
			
			clientState.sendMessage(new InputMessage(Vector3f.ZERO, torque));
		}
	}
}

[/java]

Hm, why are you sending the client the torque?

Synchronizing a full blown physics engine over the network is hard.

I’d ask yourself if you really need a full blown physics engine for a space game. Space games are nice because they usually have lots of wide open spaces and object interactions are pretty simple. Might make more sense to build your own light weight physics layer for your game.

@Empire Phoenix said: Hm, why are you sending the client the torque?

clientState.sendMessage sends the given message to the server :slight_smile:

@aaronperkins said: Synchronizing a full blown physics engine over the network is hard.

I’d ask yourself if you really need a full blown physics engine for a space game. Space games are nice because they usually have lots of wide open spaces and object interactions are pretty simple. Might make more sense to build your own light weight physics layer for your game.

In my first version of my space game I did that and it was a complete mess. I had pretty nice and fast collision detection but collision responses always felt weird, i.e. objects pounced back in a unnatural way etc. I do not want to program this stuff ever again if there is a working and testet alternative.