Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Client-Side Prediction #34

Open
erincatto opened this issue May 2, 2023 · 5 comments
Open

Support Client-Side Prediction #34

erincatto opened this issue May 2, 2023 · 5 comments

Comments

@erincatto
Copy link
Owner

  • Determinism
  • Rollback
  • Contact Begin/End events
@jordo
Copy link

jordo commented May 4, 2023

A comment here from our use-case that we've been dealing with in various ways over the past few years... For reference, a project with Box2D Client-Side Prediction implemented here (Also added SDF collision fixtures to this as well): https://rocketbotroyale.winterpixel.io

But for client-side prediction to work, we need to arbitrarily rollback the world to some given state and re-simulate with step() X number of times applying some set of saved inputs per tick. Let's call this process, Rollback & Resim. This rolls the simulation back to some point in time, then fast-forwards back to the real time stream.

This actually works very well in Box2D with a few caveats...

  • The first is that since the engine uses linked lists, the order of the internal lists is important for determinism. So if you're rollback involves say removing and adding bodies to the simulation ( think rockets, missiles, etc ), then after the reset you need to ensure the internal b2 lists (b2body list for example), is in the same order as the server. We can work around this with sequence number on body creation and insertion into world. And a simple re-sort on the reset.

  • Second is that resetting the world via this mechanism usually means rebuilding the contact list. Before we reset anything, we ignore all contact listeners (Begin,Pre,Post,End), and disable any processing there. Then we reset/recreate all bodies and fixtures to known state, sort internal lists, then manually call FindNewContacts() to build the internal contact list. The idea here, is that after FindNewContacts() is called on the client's reset, the client's world state should be the same as the server after it's regular step function(). We've found this generally to be correct with the exception of Begin/End contacts state being contact state that crosses step boundaries. It would be ideal if you could remove this kind of inter state contact dependency as it makes the reset part much easier. Right now Box2D never will emit a BeginContact and EndContact in a single Step even after the solver finishes there is no AABB overlap, i.e. the contact doesn't exist. The EndContact callback is only emitted on the next Step call, which means there is leftover contact state between two world iterations. I think eliminating any type of inter step state would make it easier to reset simulation to arbitrary points in time, in which client-side prediction with rollback & resim becomes a lot easier to do.

@jordo
Copy link

jordo commented May 4, 2023

Just another comment as this scenario may be helpful... We run our cloud gameservers single threaded because we generally want our server load to be as non-volatile as possible. i.e. I'm not sure we would WANT to solve our physics simulations multithreaded there. For our use-case we schedule each gameserver a core (well less than a core in all honesty), but we definitely limit the cpu on each gameserver to 1vCPU.

So multi-threaded-solver... Very welcome on a client device, but i'm not sure I would want to run the solver on multiple threads on a gameserver. We'd want to pin the cpu through the simulation on a single core so we can balance load across a cloud VM with reasonable cpu scheduling assumptions for each gameserver process.

Would be great if when thinking about determinism and threading, if some different configurations were available.

@Ughuuu
Copy link

Ughuuu commented Nov 1, 2023

Will this be targeted for 3.1? Or is it more of an investigation task?

@erincatto
Copy link
Owner Author

I'd like to get this in for v3.1. However Rollback might be v3.2. It is a big feature.

@jordo
Copy link

jordo commented Mar 4, 2024

This has gotten me thinking again... Just a note for any potential rollback feature in b2c, but we recently shipped another game with client reset/rollback, however on this game we had a lot of objects that were stacked and there just wasn't a decent simulation that didn't kill our cpu without warm starting enabled.

So we have it enabled and eventually our client simulations just kinda even out, but this has gotten me thinking about warm starting in general and how it would apply to rollback/reset simulations as well as any other internal 'state' memory that the engine maintains across Step()s. I don't have anything to really contribute here other than recognizing that once you get into the internals of warm starting, determinism and rollback becomes seem very complicated when it involved any internal state shared across world ticks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants