I am using the following approach in Carnage:
Basic packet logic:
- All UDP. Packet design takes into account for unreliability.
- 'Update' packets have position and state of objects, bullets, logic etc. sent at set rate, up to 10 times per second.
- Additional 'Instant' packets sent for new events (key presses, destruction/creation of objects) as soon as they happen to reduce event lag
- Update packets also contain Instant packet data to get around potential loss of data so events aren't lost.
- Both client and server send at same rate
Movement method:
- Server has responsibility for everything from entity creation (bullets etc) to position and collision. Client also does this stuff but is overridden/updated by server's version.
- All objects have a visual and actual set of positional data. Actual is where the model/entity is and can be updated without interpolation, and Visual is where the visual model is placed and is interpolated towards the Actual data for smoothness.
- Actual positional data is updated instantly by packets, so when the server tells the client a player is at XYZ, Actual data is set straight away.
- Visual positional data is where the model is actually placed and is constantly interpolating towards Actual data. This avoids all jumping and maintains smoothness.
Prediction and input:
- When a player hits a key it is instantly processed client side and updates Actual data. So press forwards and player goes forwards straight away, then sends Instant packet to server.
- When data comes back from server this can override Actual data as normal, so while client key presses have effect straight away, the server has the final word.
- Client also continues to move Actual data of other entities like bullets and players based on their trajectory and simple prediction algorithms if necessary.
The separation of Visual and Actual positional data gives for a really smooth visual representation of the world. To avoid the visual model lagging behind it's actual position you need to get the interpolation right.