Closed dasilvacontin closed 8 years ago
I could make the server correct the clients every now and then, but it's sad since ideally it should work without correction if it were deterministic.
Hello! p2.js is deterministic. If you do the same things with your physics world on the same physics tick, everything will be the OK. For example, this would produce the same state B everywhere, assuming state A is the same:
// --- State A ---
world.addBody(body);
world.step(1/60);
body.applyForce(...);
world.step(1/60);
// --- State B ---
If you update your physics in a render loop, you are in a tricky situation. You have to make sure that the number of physics ticks are the same and that the .applyForce() are run in the exact same tick (world.time must be equal).. Not sure how to do that, if you want a continuously running simulation on both server + client.
"Synchronizing everything" is a bit of work. p2 was not really designed to do this, and it's probably very complicated to do in any physics engine. You have to synchronize the ticks (as mentioned), all bodies, all contacts, all constraints, all springs, the solver data, the overlapKeeper data (if you use the contact events), the order of all the world objects, the order of the attached event listeners, the order of bodies+shapes+etc... Perhaps also the id's of the shapes+bodies, I'm not sure.
Is it not possible to just re-create the World instance (and everything in it) and simulate it a while for each turn? Like,
// --- State A ---
var world = createWorldFromState(turnState);
// ...simulate the world for 100 ticks in a render loop:
// if(world.time < 100*fixedDeltaTime) world.step(fixedDeltaTime); else endLoop();
var newTurnState = getStateFromWorld(world);
// --- State B ---
This would probably be the easiest approach. And the turnState
above would not have to contain much at all, maybe only a vector3 if a position is all you're interested in for your game. Storing everything could potentially be a lot of data (for example, you never know how many contacts that can be generated during a turn).
Not sure if this is the reply you expected, but I'd really recommend re-creating the physics world instead of synchronizing everything: the amount of work is greatly reduced, and unexpected state drift can be completely avoided this way.
Hi @schteppe! Thanks, and good to hear!
I'm already handling tick synchronisation. I synchronize the game's start timestamp, and I step the logic/physics/p2 TIME_STEP
ms for every TIME_STEP
ms that have passed since the game's start.
I tried the re-create the World instance solution. I don't remember if it worked, because the game would slow down / freeze after a few seconds – probably due to the garbage collector after creating and dumping too many js objects (worlds and bodies)...
Is there a method to reset and reuse a p2.World or p2.Body? To make an object pool, or some deterministic instance reuse system. Otherwise I could try creating my own. At least I now know that this is probably the best solution / direction to take. :)
All right cool! Freeze after a few seconds? That must be a lot of garbage. How often do you have to sync / rebuild? If it's necessary to rebuild more than once per seconds or so, then I understand, and reusing the objects is probably the way to go.
(You should in any case look for memory leaks. Don't keep references to things that should be garbage collected etc).
What kind of game is this? It's not turn based and not realtime multiplayer, maybe something in between?
You may be interested in how Unreal does their sync (though it's just approximated client side prediction and having the server authoritative): https://udn.epicgames.com/Three/NetworkingOverview.html#Physics
There's no built-in way to reuse the objects.. you'll have to clean them up yourself. Check the source and go through all properties.
@schteppe it's a racing game: http://ag-drift.herokuapp.com/ (left-arrow/right-arrow to turn 90 degrees, up-arrow for main thrust, A/D to lean to the sides, S to boost)
The ghost is the server's simulation, for comparison with your local one.
The game will have to re-simulate every time it gets an event meant for a past turn... which will be always, since TIME_STEP
is 16ms right now.
I'll take a look at the link :) Sounds like my plan B: only the server does the real simulation and it corrects the clients every now and then. Still, my physics step is very short (small map, not many bodies, all rectangles), so there should be no problem in re-simulating 30 turns in a go on the client, for correction.
There's no built-in way to reuse the objects.. you'll have to clean them up yourself. Check the source and go through all properties.
I'll try that. :)
There must be something wrong with my code, because I'm now creating new instances of World
and Bodies
for each Turn simulation, and it still doesn't sync correctly.
I'll keep digging / debugging.
But my doubts are solved – thanks much! :)
Found an important bug :) (kinda typo / dislexia)
I wasn't applying events to the desired Turn
, but instead to the current one, always. https://github.com/dasilvacontin/ag-drift/commit/e0bdf8c01b8a54f05024f7272965f48224f5426b
Lesson: use good variable names that describe the content. i.e this.currentTurn
instead of this.turn
. Reminds me of GitHub when it asks you to write the name of the repo you are deleting.
@schteppe Are Shape
s and/or Material
s ever mutated by stepping the World?
Hi!
I'm building a multiplayer game using
p2
. EveryTurn
is generated using the previousTurn
+ theGameEvent
s received for the previousTurn
. My bodies have fixed rotation, and in eachTurn
I store theposition
,velocity
andangle
properties of each body.The simulation using p2 runs both on the clients and on the server, so that – simplifying – the server only has to broadcast events and the turn index they should be applied to. If you get a
GameEvent
for a previous turn, you apply the event and re-simulate the game from thatTurn
.For this to work, the
evolve
function of theTurn
s should be deterministic. However, I'm afraid that I'm not storing enough ofp2
's state and that (worse even for me)p2
relies on storing state for the past step.Also, I noticed that the order of the bodies in world's bodies array matters, but I'm already handling that. I have issues with simple stuff as applying forces – it sometimes moves more in one simulation than in the other.
How much state
p2
relies on in order tostep
the world? InTurn::evolve
I need to be able to reset the bodies (since they are shared to save up memory), and – using theTurn
's info – restore the relevant/important properties to correctly and deterministicallystep
the world.Thanks for the library and the help! :)