Progress Video

but people would notice their sims jitter back a few frames

The movement should be free from the VM syncing. I suppose movement implies a queued interaction, but the client should start moving the sim using pathfinding before it gets a possible correction(s) from the server. This is the standard way of handling movement, and avoids lag.
 
Think of the problem this way then:

Sim A is using a "lottery" object.
On frame 52, their vm has the object give them the 1 million jackpot due to the "random" number it came up with.
However, due to a lag spike they haven't recieved an interaction performed on frame 51 yet.
After a while they recieve the interaction, the vm backtracks but this time the random seed has been changed by the missed interaction and they do not win the jackpot

They're very confused when the money suddenly disappears, as the vm has recalculated the result.

This is probably why we'll have to do scheduling either per frame or in small chunks. The Sims might not be a twitch shooter but it is still a realtime game, so we can't have too large of a buffer between the server and client or people will notice lag and it won't be enjoyable to use.
 
RHY3756547 said:
The idea is that all objects are kept in sync implicitly because the VM gets to the same result on each machine - it only has to send the vm state at the start or if something terrible happens. This is like how console emulator netplay or TASing work - these all rely on the machine getting to the same result from a set of inputs. The inputs would also govern the vm random seed, to avoid any two machines coming up with different answers.

Once every 100 ticks is megaslow - that's several seconds people would need to wait for their queued action to be returned to their vm. Since user queued interactions don't happen often, we could instead use the 100 tick packets as a sort of "keep-alive", this would perform the vm crc check to see if anyone desynced, and then instead send interaction packets as soon as user queued interractions arrive at the server. All normal interactions should be queued by all vms in the same way from user interactions, so we really only need to send user queued interactions.

Only problem here is, what if user x's pc executes tick 52 before it gets the information on what to do on tick 52? We could make the vm backtrack, but people would notice their sims jitter back a few frames. We really don't want people waiting ages for the server to ping them back their interactions, so the ticks either need to be sent often or like this. The packets would be quite small as they would only contain new user interactions (array of Object ID + interaction to perform) or even just be empty to keep a steady flow of packets. We can still do the crc check every large number of ticks as it isn't important to gameplay but is instead a stability check.

We COULD do this p2p, but we would still need to sync VMs against a "host", to keep everyone from seeing different things. But this means that the host vm would be able to do anything it likes, ie trick the simulator into giving them a million simoleons as there's no reliable reference to check against. The host would likely be the first roomie to log into the room, and it would be passed down to other roomies on disconnect.

Yea that's it exactly. You keep the VM perfectly in sync so that you only have to send the inputs. If you don't do this then you need to sync hundreds of object state variables (there are hundreds per object) etc, becomes quite difficult and high bandwidth. The server should run a full VM so it can make tie breaking decisions and also have a view of the world to serialize on room connect.

FYI the video i showed was running at 30 ticks per second. The VM is a little different to a CPU in that you can execute more than 1 primitive per tick. I tried it with 1 instruction per tick and everything runs much slower than in the game. So i tweaked the engine so that a primitive can return GOTO_TRUE, GOTO_FALSE, GOTO_ERROR, GOTO_TRUE_NEXT_TICK, GOTO_FALSE_NEXT_TICK. Things like animation and sleep use the next tick returns.This may not be the right way but it seems to give a similar result. In Edith that Fatbag cracked there is some UI as well that says "# Primitives" in a context that makes it sound like you can have multiple primitives per tick.

100 is probably too high, was just an example, . I think you should pause the VM if you dont have the next sync packet. Aka execute to as many ticks as the sync packet allows and then pause. The sync packet can kind of be a guarantee that I (the server) wont insert any new interactions until tick X. That means you can run the VM locally until tick X confidently. I remember when I played TSO with an ADSL 1 setup it would pause every now and then and then play fast to catch up. This would be quite easy to implement.

RE queuing interactions. The reason to involve the server here is to validate the interaction but more importantly to pick a sync packet that the interaction should be inserted on. Every client must insert a behavior on the same tick or it would get out of sync.

If you sent a sync packet every 30 ticks that feels reasonable. You can still show the user their local interaction list in realtime. It just may take up to 1 second to see this start. This becomes a tuning exercise then.

Similarly, I think you would need to do the same with lot architecture changes. If i place an object or a wall (aka obstacles) i think this should be done in a sync packet too. Otherwise the routing algorithm may have different obstacles depending on when it got the "add object" "add wall" command.
 
Afr0 said:
but people would notice their sims jitter back a few frames

The movement should be free from the VM syncing. I suppose movement implies a queued interaction, but the client should start moving the sim using pathfinding before it gets a possible correction(s) from the server. This is the standard way of handling movement, and avoids lag.

The way I see it is routing has consequences. It may not seem like a big deal if you go a slightly different route on two clients but it has an effect later on. Lets say i move my character to the opposite side of the lot. On 1 user's PC there is an obstacle and on another there isn't. So, my sim arrives at the destination at a different time on two users PCs.

That means that the next interaction on my list (study cooking) executes sooner on PC A while PC B is still routing. Lets say another Sim was going to read the same book from the book shelf. On PC A they would fail because I already took the book. On PC B that user gets the book and my sim fails to get the book. We now have a very out of sync world.

The aim isn't to sync routing, this is a lot of data being streamed at 30fps. But you can design the algorithm such that when given the same obstacles the same route is found then you can route on the client side and still have it be in sync because you only allow obstacles and interactions to be introduced at specific times.
 
Yeah, ok, I looked into it, and hplus0603 (an authority on networking in any situation, given he's the admin of the Multiplayer and Network Programming board at the gamedev.net forums) says that:

If you are using lockstep simulation, then path finding has to run on both server and client -- the only command that gets transmitted is "this unit, start moving to that destination, at time T." This saves a lot of network bandwidth, but means that everybody have to run the pathfinding code (and thus use more CPU).

I think what you guys are proposing is just that: lockstep simulation. However, in the same post, he also says that:

If you're doing entity updates instead, then pathfinding only needs to come from the controller for the entity (which might be the client controlling the entity), as long as each entity movement is checked for validity.

This is what I was thinking of. But it might not be the best approach for this game.
 
Afr0 said:
Yeah, ok, I looked into it, and hplus0603 (an authority on networking in any situation, given he's the admin of the Multiplayer and Network Programming board at the gamedev.net forums) says that:

If you are using lockstep simulation, then path finding has to run on both server and client -- the only command that gets transmitted is "this unit, start moving to that destination, at time T." This saves a lot of network bandwidth, but means that everybody have to run the pathfinding code (and thus use more CPU).

I think what you guys are proposing is just that: lockstep simulation. However, in the same post, he also says that:

If you're doing entity updates instead, then pathfinding only needs to come from the controller for the entity (which might be the client controlling the entity), as long as each entity movement is checked for validity.

This is what I was thinking of. But it might not be the best approach for this game.
The pathfinding should actually only be caused by interactions occuring, so it would be implicitly synced with the rest of the simulation. If the order pathfinding is worked out for sims is the same on all machines, no two machines should decide to do different things in the case of a routing collision either.

The trickiest part of this will probably be serializing the entire vm + animations + pathfinding and sending it out to people connecting. This might be very slow to generate or very bandwidth heavy depending on the data structure, even with compression, and it would need to encode and decode absolutely everything correctly or the client would be desynced to start. On the other hand, we can use the same format that lots are stored in serverside, as all objects need to keep their state on reload anyway.
 
Yea i agree, its a lot of data to serialize. You could connect to start receiving sync packets and then have the server send a lot dump. Then you can combine the two to get current state.

Maybe you could cache some of the serialized data for architecture or objects that don't update very often?
 
Is the lot rendering branch at the same level of development as the one shown in the video? I was thinking of implementing the z buffer on sprites by using SV_Depth (HLSL equivalent of gl_FragDepth).
 
Nah, the one in source control is really old. I got quite far with it and then had an SSD and my local source control drive die with a PSU fail so lost that code :(. I had to redo it so didn't get too far with SimAntics.

I'm using HLSL pixel depth. Its slow. I think it can work but I need to introduce some sort of caching. I've attached my current shader to this post.
 

Attachments

  • 2DWorldBatch.zip
    1.2 KB · Views: 6
Here's my attempt at getting the Z aligned correctly. This makes a few assumptions: that backPosition.xz += 2.5 will move to the back end of the tile, that the world position is the position of the center of the tile (at least, the z of it is) etc.

I made a few measurements so there are some pretty rough float values in there.

This relies on the observation that the Z buffer can be extended from a plane at 30 degrees to the vertical and the proportions will be correct. I renamed a few of the variables to fit what I was using them for.

Let me know if it works - might have completely messed something up/assumed wrong.
 

Attachments

  • 2DWorldBatch.zip
    1.4 KB · Views: 8
Bonus: using this idea to apply a sprite to a displacement map in blender


As for a "cache", the original game didn't actually redraw the whole screen every frame, just the sections that were flagged as being "changed". We could probably do something similar for this.
 
That's very cool Rhys. Nice way to get some rough geometry for object clones.

I need to try find some time to better understand how the 2D and 3D world come together. Not entirely sure how many world units there are per tile (2.25 or 2.5 I think). Or the exact view, projection (with compression) values.
 
I worked the scale out when developing vitaboy-webgl. If the person is drawn unscaled, the tile below them should be 3x3 units. This version is pretty old, but it clearly demonstrates the 1x1 tile (or at least a triangle of it) below the sim with a diagonal width of about 128px. I compared it against an image of the sims in near zoom and it seems to be perfect.

https://dl.dropboxusercontent.com/u/122 ... index.html

If the scale were changed to this in the simantics demo, the shader I posted should be modified to use 1.5 instead of 2.5.

EDIT: here's a comparison:
comparison.png
 
Cool, ill try your numbers at the weekend. Did you compress the projection? There is some documentation somewhere that says that was done for easy GPU uploading to keep everything a power of 2 (i think).
 
Isn't that only the textures? I don't think any other geometry or information benefits from being power of two when sent to the gfx card.

The character textures are very helpfully already power of 2, since they did render the sims on GPU in the original game. (old gpus didn't support non-power-of-2 textures)
 
Hm, maybe. I had thought since the 2D assets were compressed a bit to give better texture uploading that we would need to do something similar to the 3D world to have it line up accurately. Perhaps I'm being stupid.
 
There are a few things you can do such as avoid recreating the entire vertex array every frame, and I think there are ways to keep a cache of the data on the gfx card so you don't have to send it every frame too. (does it do this automatically? maybe i should stop sending my city vertex data to the gfx card like crazy and see if it still works)
 
You need to use the VertexBuffer and IndexBuffer in xna. It isn't automatic. Unfortunately because the sim meshes get mutated so often it makes less sense for them.
 
Don't you have to use VertexBuffers to stream vertex data at all? What I meant was, is there a way to keep the data on the GFX card without streaming it every time?

I am only generating the VertexBuffer once for the City mesh, but this approach seems to cause an AccessViolationException on minimize -> restore.
 
Back
Top