Using ncollide in less stateful ways


#1

I am using ncollide in a fast-paced multiplayer network game. The server is authoritative, simulating the game and sending the world state to clients periodically in snapshots. Clients perform local prediction of the state of player-owned entities according to local input, so clients maintain their own copies of the CollisionWorld. In case of a local prediction error (or lag), clients reset the state of objects to the correct state, as received in a snapshot from the server.

After a client has reset its local state to the server’s state, simulation should proceed deterministically on server and client (up to numerical differences, and unless another prediction error occurs). However, I’m not sure if ncollide's DefaultNarrowPhase contact point generation will be equivalent on server and client after snapping/resetting the world state. As far as I understand, IncrementalContactManifoldGenerator for example will keep track of state over multiple time steps. Then, the client’s contact generator might remember some state from the prediction, even after resetting to the server’s state, causing simulations to diverge.

So essentially I am looking for a way to use ncollide in a way such that contact point generation depends only on the current state, completely disregarding information from previous frames. I would prefer to continue using CollisionWorld. Would it make sense for me to implement my own NarrowPhase, which does not keep contact generators over multiple frames, and instead calculates contact points from scratch in every frame?


#2

I don’t have any experience on network game development, so I can only comment on how to get rid on the dependence of contact determination result wrt. previous states. Is you correctly understood, the IncrementalContactManifoldGenerator is the cause of this dependency. To get rid of this, it is best to:

  1. Write your own implementation of the ContactDispatcher trait. For example, you could copy/paste the DefaultContactDispatcher and adapt it according to your needs. In particular you probably want to call .set_always_one_shot(true) on each instance of the OneShotContactManifoldgenerator (which uses IncrementalContactManifoldGenerator internally). This will force it to recompute the contact manifold independently from previous frames.
  2. Instantiate a DefaultNarrowPhase, giving your contact dispatcher as argument to the constructor (as well as the DefaultProximityDispatcher for the second argument).
  3. Call .set_narrow_phase on the collision world.

#3

Thanks a lot for your reply. This is exactly the information that I needed — I had not seen set_always_one_shot yet.

I have a similar concern about SupportMapSupportMapContactGenerator. It seems that this could potentially carry some state in the simplex field. I’m not sure how the &mut self.simplex parameter is used in support_map_against_support_map_with_params though. Is there a dependency on the previous state here or does this just serve as some kind of buffer?


#4

As long as the shapes you are using are polyhedron, the old state of the simplex should not affect the result because the iterative CD algorithm will converge toward the exact same points. However, if other shapes are used, results might indeed differ slightly, but not less than a very small epsilon, so I don’t think this will make the various instances of the simulation to diverge from one another.

If this is an issue in your case, you may replace SupportMapSupportMapContactGenerator by your own structure that does the same, but that always set initial_direction to None there.


#5

Great, that should be fine. Thank you!