[hlcoders] Player Physics
I'm going to put this here both as a note to myself and as a way of getting commentary on this from anybody who is interested in player physics, server-side. Please do correct me if anything is wrong here. So how does player physics work? At the very base are the CUserCmds that are fed into the server. CServerGameClients (I will be referring to these by implementation, not interface) gets these through ProcessUsercmds() and passes them on to CBasePlayer's ProcessUsercmds(). They're buffered there. Now there comes a time when GameFrame() is called in CServerGameDLL. The call chain of interest is: CServerGameDLL::GameFrame() Physics_RunThinkFunctions() Physics_SimulateEntity() CBasePlayer::PhysicsSimulate() Now is where the CUserCmds are actually used. For each CUserCmd, this happens: Each CUserCmd is passed into CBasePlayer::PlayerRunCommand(). CPlayerMove::RunCommand() is called, calling these three: CBasePlayer::SetupMove() [prepares the CMoveData] CGameMovement::ProcessMovement() [updates pos, vel, etc.] CBasePlayer::FinishMove() [copies the updated data back] The VPhysics shadow object is updated with new pos and vel. Ok then, all CUserCmds are processed. Then, CServerGameDLL::GameFrame() calls IGameSystem::FrameUpdatePostEntityThinkAllSystems(), and this happens: CPhysicsHook::FrameUpdatePostEntityThink() PhysFrame() In this function, the entire physics environment is first simulated. Then, CBasePlayer::VPhysicsShadowUpdate() is called. This is where the fun stuff is, where the player entity's position is reconciled with what the physics system thinks it is. Then, after all this, data is sent off to the client, yadda yadda. SO What's the moral of the story? Here's what I get out of the code: For player movement, what happens is that player's aren't really physically simulated at all! Everything (movement, gravity, swimming, etc.) is handled by the CGameMovement class. With every CUserCmd: 1. CGameMovement simulates the player and derives new pos/vel. 2. A physics shadow object is updated with the new pos/vel. This happens for every CUserCmd. Then, after all the CUserCmds have been processed that frame, the physics simulation actually occurs. At this time, _all the VPhysics shadow updates for that frame are processed at once_. Let's say, there were 3 CUserCmds from the player processed this frame, then the 3 shadow state updates will happen sequentially in the same frame. After physics simulation occurs, the player entity is reconciled with the VPhysics entity. Any VPhysics damage or position/velocity differences are copied back into the entity. Then the ent data is packaged and shot off to the clients. QUESTIONS 1. Are we guaranteed only 1 CUserCmd processed per frame? OTHER THOUGHTS I haven't seen any GoldSrc code before, but I suspect that the only way to _exactly_ reproduce GoldSrc player physics would be some crazy coding in VPhysicsShadowUpdate(), or even stripping the player of VPhysics simulation entirely. You'll likely do a _lot_ of copy-pasting of GoldSrc code either way. I think, to be honest, that you'll have to go the latter route (nuking VPhysics) because there's no way that VPhysics will exactly reproduce GoldSrc physics. You'll have to do it all in CGameMovement. John Sheu ___ To unsubscribe, edit your list preferences, or view the list archives, please visit: http://list.valvesoftware.com/mailman/listinfo/hlcoders
Re: [hlcoders] Player Physics
Wow! For once, an informative, accurate and thoughtful post in a list that usually contains nothing but cruft! You caught me off guard. Thanks, John. John Sheu wrote: QUESTIONS 1. Are we guaranteed only 1 CUserCmd processed per frame? I don't think so. I think that in high-lag situations the net code may receive more then one user command in the space of one frame. There's definitely the capacity to process more then one command, so I assume that Valve wrote that because there is a possibility of that happening. OTHER THOUGHTS I haven't seen any GoldSrc code before, but I suspect that the only way to _exactly_ reproduce GoldSrc player physics would be some crazy coding in VPhysicsShadowUpdate(), or even stripping the player of VPhysics simulation entirely. You'll likely do a _lot_ of copy-pasting of GoldSrc code either way. I think, to be honest, that you'll have to go the latter route (nuking VPhysics) because there's no way that VPhysics will exactly reproduce GoldSrc physics. You'll have to do it all in CGameMovement. Actually, CGameMovement is pretty much a direct port to C++ of HL1's C pm_shared code. Most of the physics and alot of the code has been retained. The only real functional difference between HL1 and HL2 movement code is the player vphysics shadow, and all that does is move the player around a little bit if his movement interferes with vphysics. To get movement code identical to HL1, effectively all you would need to do is remove the code from VPhysicsShadowUpdate(), but then your player would lose all interaction with physics objects, and why would you want that? -- Jorge Vino Rodriguez ___ To unsubscribe, edit your list preferences, or view the list archives, please visit: http://list.valvesoftware.com/mailman/listinfo/hlcoders
Re: [hlcoders] Player Physics
On Fri, 2006-03-31 at 15:09 -0500, Jorge Rodriguez wrote: Wow! For once, an informative, accurate and thoughtful post in a list that usually contains nothing but cruft! You caught me off guard. Thanks, John. All hope is not dead on teh intarnets. A few notes: Correction: the shadow is not updated for every CUserCmd, but for every _batch_ of commands. If the user's commands are dropped for some reason, then the player is not simulated at all server-side. (His physics shadow doesn't move, but it still exists). When the commands finally to arrive, they are then all simulated in the timespace of a single frame. What exactly are the non-obvious differences between the Player/Vehicle/Shadow/Motion controllers? Actually, CGameMovement is pretty much a direct port to C++ of HL1's C pm_shared code. Most of the physics and alot of the code has been retained. The only real functional difference between HL1 and HL2 movement code is the player vphysics shadow, and all that does is move the player around a little bit if his movement interferes with vphysics. To get movement code identical to HL1, effectively all you would need to do is remove the code from VPhysicsShadowUpdate(), but then your player would lose all interaction with physics objects, and why would you want that? That was essentially my point. It's not my problem, though, it's KMod's problem. I just brought it up because I noticed a few mailings on this list from them. John Sheu ___ To unsubscribe, edit your list preferences, or view the list archives, please visit: http://list.valvesoftware.com/mailman/listinfo/hlcoders
RE: [hlcoders] Player Physics
This is a pretty good summary, but you're missing a couple of things: For player movement, what happens is that player's aren't really physically simulated at all! But your own explanation says this isn't true: After physics simulation occurs, the player entity is reconciled with the VPhysics entity. Any VPhysics damage or position/velocity differences are copied back into the entity. So reconciliation could mean that the vphysics data is copied over to the game - which means the original game simulation is replaced with the physics simulation. It could also mean that the vphysics data is thrown away, or blended into the result. It's much more correct to say that the players are always simulated in game code AND vphysics code. Then the results are selected or blended based on heuristics. Gravity runs in both simulations - the output position of the game physics provides the input direction for the vphysics simulator. Correction: the shadow is not updated for every CUserCmd, but for every _batch_ of commands. correct. When the commands finally to arrive, they are then all simulated in the timespace of a single frame. not correct. float flSecondsToArrival = ( ctx-numcmds + ctx-dropped_packets + additionalTick ) * TICK_INTERVAL; The shadow is updated once, but the resulting output target is reached over some amount of future ticks. What exactly are the non-obvious differences between the Player/Vehicle/Shadow/Motion controllers? Player and shadow controllers are nearly identical. The idea is to solve an overdetermined system for impulses that will cause the physics object to arrive at the target position orientation at some point in the future (usually the beginning of the next tick). Vehicle controllers include a vehicle suspension, steering, and engine model - and are generally forward controllers (you give them input and they generate physics, they don't solve for impulses). Motion controllers are just generic hooks to let you introduce your own impulses to the object at the time that it is integrated. You implement IMotionEvent and add velocity to the object to steer it. A shadow controller is a motion controller with some specific behavior that chases targets. To get movement code identical to HL1, effectively all you would need to do is remove the code from VPhysicsShadowUpdate(), but then your player would lose all interaction with physics objects, and why would you want that? sv_turbophysics is effectively this plus some forces that push you out of moving physics objects. Short of predicting the simulation on the client, this is a reasonable tradeoff once latency is high enough. Jay ___ To unsubscribe, edit your list preferences, or view the list archives, please visit: http://list.valvesoftware.com/mailman/listinfo/hlcoders
RE: [hlcoders] Player Physics
On Fri, 2006-03-31 at 18:19 -0800, Jay Stelly wrote: So reconciliation could mean that the vphysics data is copied over to the game - which means the original game simulation is replaced with the physics simulation. It could also mean that the vphysics data is thrown away, or blended into the result. It's much more correct to say that the players are always simulated in game code AND vphysics code. Then the results are selected or blended based on heuristics. Gravity runs in both simulations - the output position of the game physics provides the input direction for the vphysics simulator. Perhaps I should have clarified. As I see it, the CGameMovement code computes an end position, given a start position, and feeds that target into the VPhysics system. The VPhysics system then does its best to reach that target. The difference between the VPhysics position and the CGameMovement position is then heuristically reconciled. The shadow is updated once, but the resulting output target is reached over some amount of future ticks. That's what I meant; perhaps I should have said that explicitly. Player and shadow controllers are nearly identical. The idea is to solve an overdetermined system for impulses that will cause the physics object to arrive at the target position orientation at some point in the future (usually the beginning of the next tick). Vehicle controllers include a vehicle suspension, steering, and engine model - and are generally forward controllers (you give them input and they generate physics, they don't solve for impulses). Motion controllers are just generic hooks to let you introduce your own impulses to the object at the time that it is integrated. You implement IMotionEvent and add velocity to the object to steer it. A shadow controller is a motion controller with some specific behavior that chases targets. Those two paragraphs are worth their weight in gold. Thanks Jay :) John Sheu ___ To unsubscribe, edit your list preferences, or view the list archives, please visit: http://list.valvesoftware.com/mailman/listinfo/hlcoders
RE: [hlcoders] Player Physics
A few more questions: So, as I understand it, how the player controller works: If there is no backlog of CUserCmds, then the player is simulated by CGameMovement and VPhysics for one tick, and at the end of the frame the two are reconciled. If there is a backlog of CUserCmds, then the player is simulated by the CGameMovement as before. The player shadow is given a set of intermediate targets (based on the positions calculated at the end of every batch of CUserCmds), and then this is VPhysics-simulated in one frame, but with a time delta to match the difference between the first simulated backlogged frame and the last simulated backlogged frame. Given that, as said, the player and shadow controllers are almost identical, I conclude that the shadow controller works as well by setting a bunch of intermediate states and associating a time with each of them, then simulating it all in one frame. Is that correct? Also, am I right in assuming that the underlying IPhysicsObject functions still work? (e.g., SetVelocity(), etc.)? ___ To unsubscribe, edit your list preferences, or view the list archives, please visit: http://list.valvesoftware.com/mailman/listinfo/hlcoders