Design Some multiplayer design questions

XirmiX

Member
I've made some progress with networking in the past few weeks. Though I'm sure I'll learn some more things along the way, there are some things I wonder about in terms of the server-client design that I would like to know more information about.

So, the end game idea for my game is to have a site, which acts as a central connection for clients to see listed servers and for clients to be able to chat with other clients outside of servers. The servers could be hosted by players to set up matches and clients could join these servers. I'm thinking about offline modes as well and perhaps bots, but that's way down the line, I'm only approaching the point where I can port what I've done in a physics-only build of the game I did like a year ago.

In any case, here are a few questions that I'd be glad to get answers for (the first one might be a bug actually):
1) Temporarily invisible players! When running a server, through the debugger, I can see for all the clients what client is (currently generated by the server) they are loading information about, so, when a new client joins, you need to be able to see previously joined clients right out the gate, right? Well, it works, but, once you get to the third player, the third player won't see the first player until the first player moves/rotates after . The third player does get data about the first player, like the id and coordinates and everything are received, as the debugger shows, but it just doesn't see the first player until the first player does an action, even if it's just rotation.

My question is whether this is normal for multiplayer games, and whether it matters? It doesn't seem like It'll matter in the long run, though it would be good to fix it, if that is a bug that can be fixed. It would rarely come up as a problem, if ever, the way I've planned to have things done, but it would be good to fix none-the-less.

Yes, the players do stay visible after they move. This isn't an issue for the first two players that join (though different code is handled when a second player joins than when a 3rd or a 4th player joins, so this could have something to do with it), so that's why I'm wondering.

2) Physics! I've heard someone previously mention that handling physics by the server would be a bad idea. Currently I have clients having physics rooms where their player object is the only physics one and all other players are just sprites of a different object with barely any code in it, as the server sends information to your client about other clients' actions and such, so they move without having any code in them. Spoookyyy!

I have two issues I find with having clients handling physics instead of the server:
* I expect it to be much more difficult to code it for the clients to be able to handle physics when interacting with other players. I'm not even sure whether or how you would be able to, say, push the player object of another player if your player object is a physics one, theirs is one, but theirs on your end is just a sprite and vice versa. You would still be colliding with walls and such, since you all would have your own physics walls to interact with (which is actually a point about the second issue), but interaction would be non-existent unless your representation of the other player is a physics one as well, right? Well, this could lead to some major glitch issues, perhaps, although I've yet to try this; adding a fixture to the other player representations of their playable object.

* The second issue is that this could leeway into cheating as an issue. With servers, if you change data on your server through modding and don't want to play in it, you can leave and join a server you like. But if you can, say, change the physics walls for yourself completely, you could travel through walls, essentially, ruining the game for everyone else who on the server was playing legitimately, or based on the rules of the server.

3) Map loading! I DO have a clue on how maps could be loaded. My idea of it is that you have a room within which you play, and then the other rooms act as maps, from which physics objects and textures/sprites and such are imported into the game once you launch the server (and for clients as well when they join). However, are there any other ways of loading maps, at least when using GM (I'm using GMS 1.4 myself atm)? And would the way I've described it be efficient at all? Would it perhaps be possible for the server to send object data and texture/sprite data through buffers?

4) Any other comments? Any other advice that you could share about developing a multiplayer game would be welcome; what you think are the dos and don'ts of server-client based systems or anything related to the subject matter?

P.S. Please don't say that I should just push networking aside right now and try something simpler, with all the basics I've yet to use me needing to learn about right now. For as much as it's difficult, for as much as I hate it partly, I also love networking! I get somehow excited that I can develop a multiplayer game where players across different machines can see each other, so even if I stumble a lot while working on it, I do want to work on it with the knowledge I have and gain more knowledge along the way. I have been using tips others have been suggesting to me, such as using debugging much more frequently (though break points I still have yet to get a hang of... haven't had the need to use them yet) and as a result, I actually have something to show for, aside from one minor and rather odd bug.
 

Yal

šŸ§ *penguin noises*
GMC Elder
As for physics, the idea is that the more you can have run client-side, the better. Having to synchronize hundreds of moving objects will slow down your game a ton. However, the physics system basically is unpredictable and the same starting conditions won't always give the same result, so it will desync. You basically can't use it in an online game, unless it's just visual (broken props and ragdolling corpses).

One idea for the 'invisible players' thing... have a 'dummy' action that does nothing, but fires up your 'action was performed' code. Have that automatically happen whenever a new player joins.
 

MishMash

Member
I'll try my best to provide a few suggestions for these:

1) Temporarily invisible players! When running a server, through the debugger, I can see for all the clients what client is (currently generated by the server) they are loading information about, so, when a new client joins, you need to be able to see previously joined clients right out the gate, right? Well, it works, but, once you get to the third player, the third player won't see the first player until the first player moves/rotates after . The third player does get data about the first player, like the id and coordinates and everything are received, as the debugger shows, but it just doesn't see the first player until the first player does an action, even if it's just rotation....
First of all, generally, you want to be consistent about the process that a connection goes through. This means you should try and handle it the same for whether its one player, or whether its multiple players.

For my project, when a new player joins, after connection has been verified, the first thing that happens is that the player is notified about all other connected players, and all other connected players are notified about that new player. Secondly, the position of that new player is established on the server, and that position is sent to ALL players. This way, the new player finds out about their position at exactly the same time as all the other players do. (I do the same thing for all player data, position, clothing, equips, inventory, etc;)

Whether it actually matters is a bit of a grey area, it depends how strong your server sided coding is. (I'll discuss my general approach to networking design in reply to the last thing). For your case, I don't think it actually will, but there's no reason you cant work on improving it. It's good practise to get better at these things. The more experience you have handling communication, understanding where things fail, working out why they fail, and learning about the limitations of networked programming, the better you will get. Even after a lot of experience with networking, i'm currently procrastinating the writing of a rather complex system of server-sided inventory control and a system of interactions between different clients, where some clients are also processing certain things :p!

2) Physics! I've heard someone previously mention that handling physics by the server would be a bad idea. Currently I have clients having physics rooms where their player object is the only physics one and all other players are just sprites of a different object with barely any code in it, as the server sends information to your client about other clients' actions and such, so they move without having any code in them. Spoookyyy!

...
Who told you that :p? Simulating physics on the server is exactly what you should be doing, however, You should also perform local physics simulations so that the player can get an immediate visual response. Generally speaking, the server should send physics object updates a few times a second (perhaps batching it so not all objects send at once). When a client receives an update, smoothly interpolate the drawing position of the object back, so that if it has de-synced, it'll get back to the correct position. (The updates should be frequent enough such that things never get too out-of-sync. Note; You only need to update things near the player.)

In addition to this, when talking about things like player movement, games generally do two things:
1) The player controls their own players physics but sends position and speed information to the server.
2) The server verifies if the position and speed seem reasonable based on the physics rules for the world, whether a player could have hit something. In essence, it validates the players action. IF the player has performed an action considered invalid, the server will "Rubber-band", and send the player back to the previous position.
3) Some games also implement local rubber banding. This is achieved will a similar scheme. When the player sends their action to the server, the server sends them back a new position, then the client verifies that new position locally (This is for better syncing if the position was slightly off where the server expected you to be). When this position is received from the server, it will update the player if it is too far off. Conversely, it will also track the last position sent to the server, and can potentially pull you back if it hasnt received a message from the server in a bit.

The normal rubber-banding effect you see in other games is when the player has moved some distance, packets have not reached the server, or the timing was too sporadic meaning the move was considered invalid, so the server snaps you back to where you were. (Keeping your clients position in sync with where the server knows you are).

So yeah, in review, you do both local and remote physics. The local physics improve your own visual response/control feedback, but do not necessarily dictate a guaranteed action on your screen. The server also performs these and maintains the "correct" positions, and may force re-position you. One thing I never loved was the sending of key-presses to the server and also performing local key-presses. I found that it would often get out of sync quickly, and resulted in lots of rubber banding, which is why I always opt to only send position and speed, then just perform a verification on the server.


3) Map loading! I DO have a clue on how maps could be loaded. My idea of it is that you have a room within which you play, and then the other rooms act as maps, from which physics objects and textures/sprites and such are imported into the game once you launch the server (and for clients as well when they join). However, are there any other ways of loading maps, at least when using GM (I'm using GMS 1.4 myself atm)? And would the way I've described it be efficient at all? Would it perhaps be possible for the server to send object data and texture/sprite data through buffers?
This one is hard to answer as I don't really know enough about your game. The general approach I would take here is that id tend to send the levels from the server every time, however it also depends on a few factors. For games with fixed levels, i'd only have the server send any dynamic object (as these will likely need to be tracked by the multiplayer system anyway). With that in mind, when building the levels, what you can do is build them entirely in separate rooms, then just have the clients delete all the dynamic objects, and have the server re-send them.

If however, you want a game which supports custom levels, you would need to send that data over in its entirety.

Regarding what you can send, buffers are just blocks of formatted data. At the end of the day, you can write data as small as a byte, which means you can stream whatever the hell you want. Sending files is quite easy as you can just load a file into a buffer (using buffer_load), use buffer_copy to copy that into your network buffer, and send. Can do similar things for sprites too, however it can get a little awkward to organise.

Though yeah, your assumptions were mostly fine for that. Its really doing whatevers required for the specific game in question, there's not so much a general formula that can be applied or anything.

4) Any other comments? Any other advice that you could share about developing a multiplayer game would be welcome; what you think are the dos and don'ts of server-client based systems or anything related to the subject matter?
Firstly, good job on starting networking! It is really fun as you said, and unless your project is crazy ambitious, don't listen to people who tell you to push networking aside. Nothing is harder than adding networking to a game that was not designed with networking in mind. Networking is easier (still not easy though) when each part of your game is added and networking is built-in straight away. It's a ball ache trying to add networking to a game that is finished. It is however good to make a basic engine first, or sometimes even a single-player prototype depending on the game.

My main general comment for networking is that you should always treat the server as the main place where the game processing is done. All clients are simply a unique view into the world of the server, any local processing you do is mainly there to create a better feel for the player, so that you can reduce the update rate a bit, and still have the game feel fresh. All clients really have to do is send interactions/input to the server, and receive responses.

But the main thing is just boiling down to building experience. You'll soon find ways that are more effective for doing things, and don't be afraid to sit down for a little longer
 

Xer0botXer0

Senpai
The third player does get data about the first player, like the id and coordinates and everything are received, as the debugger shows, but it just doesn't see the first player until the first player does an action, even if it's just rotation.

Yes this matters, your players will know about it. rather fix it now before putting 2000 more lines of code down.
 

XirmiX

Member
As for physics, the idea is that the more you can have run client-side, the better. Having to synchronize hundreds of moving objects will slow down your game a ton. However, the physics system basically is unpredictable and the same starting conditions won't always give the same result, so it will desync. You basically can't use it in an online game, unless it's just visual (broken props and ragdolling corpses).
Who told you that :p? Simulating physics on the server is exactly what you should be doing, however, You should also perform local physics simulations so that the player can get an immediate visual response. Generally speaking, the server should send physics object updates a few times a second (perhaps batching it so not all objects send at once). When a client receives an update, smoothly interpolate the drawing position of the object back, so that if it has de-synced, it'll get back to the correct position. (The updates should be frequent enough such that things never get too out-of-sync. Note; You only need to update things near the player.)
Which do I believe?!

One idea for the 'invisible players' thing... have a 'dummy' action that does nothing, but fires up your 'action was performed' code. Have that automatically happen whenever a new player joins.
Well, I tried making the movement function fire up at the beginning or a few frames after (with an alarm) regardless of what happens, but there didn't seem to be any change in spawning, strangely. For when a second player joins, I has server run a different code than if a third or a higher number of a player were to join. Second player joining didn't have a for loop, while the 3rd, 4th and 5th did, for obvious reasons. When I commented out the unique second-player-joining code and made the one which runs for the third and higher to also run with the second player, low and behold, the second player who joins also can't see the first until the first moves, which previously didn't need to happen; the second could see first right off the bat and issues just started to happen with the third and above. Another difference is that for the code that was explicitly for the second player joining before, there was no ds_map data retrieval code. I think the data retrieval might have something to do with this not working right, though, from debugger, I can tell that the right data is being retrieved and sent. May be ds_maps simply have some mechanical glitch that makes this happen, or for loops within GM itself that make this happen, cause data is received and sent just fine, no errors, no real game-breaking glitches per-see.

@MishMash hmm, I see about the whole server-handling. I guess the best option would be for the player to send keyboard input to the server and then the server sending the coordinates of its representation of the player character to the client to apply to its actual "playable" object, which would decrease the likelihood of high-speed cheating. I've seen in one other game where you can still move your character just fine, even if everything else is lagging and then once the lag stops, you don't get rubber-banded anywhere and server just updates your current position. This kind of a thing would require the player to have it control its own character without sending information to the server first and would make it more prone to cheating, if I'm understanding this right, correct? Well, it would be great to not have things rubber-band, but if it stops cheating, I guess there's no other way. Or, have the player not respond to key pressing if it can't update with the server, which the key-pressing one would really do. I honestly hate rubber-banding myself, worst thing ever!

The third player does get data about the first player, like the id and coordinates and everything are received, as the debugger shows, but it just doesn't see the first player until the first player does an action, even if it's just rotation.

Yes this matters, your players will know about it. rather fix it now before putting 2000 more lines of code down.
Not entirely sure how or whether that's possible, if it's due to a GM glitch. Might be just something I'm doing wrong, but since the correct data is received, by logic, it doesn't seem like this should happen. I mean, when the second player joining had the server use a different code for sending data to the other player and yourself of the other player, both that piece of code and the code that handled sending information about the 3rd+ player joining were sending stuff to be handled by the client with the same code on the client side. This has either something to do with ds_maps or for loops creating this glitch, or perhaps both together, though I'm fairly certain I did everything right, I mean, otherwise the correct information wouldn't be sent out in the first place.
 

MishMash

Member
Which do I believe?!
Well, there are a few generic comments between the two methodologies which are relevant. Firstly, both ideas mention the notion of client simulation, both also mention the idea of desync. I haven't used GM's system specifically, so am not actually sure if you even have access to be able to modify all the internal variables (though I assume you would).
My general opinion on this is that there isn't that "much" data in comparison to how high-bandwidth games are.

For a bit of info, CSGO streams around 50KB/sec of data (at a normal competitive tick rate). Overwatch is quite a bit lower at around 15KB/sec. Minecraft is an interesting one: according to a forum post, general gameplay is around 20KB/s, however during initial world load, its 800KB/s (or as fast as it can go really), and then during times when chunks are being loaded, it's around 100KB/sec

Assuming positions and speed/direction are shorts (16 bit), within a reasonable bandwidth of say 20KB/sec (which is probably about the average for modern games), you could send 10,000 values a second, assuming an average update rate on objects of 4 times a second, that's 625 objects sending 4-values. Naturally, there are other things you also need to send, but equally, you probably dont need to be updating ALL 625 objects, as only ones that have changed need updating, equally, you only need to update objects that are near given players.

It is true however that if you aren't smart about what/when you send updates that you can end up sending far too much. I'd also have to disagree with Yal as far as slowing down the game, however the comment on the physics system being unpredictable may mean that its not sufficient to only send position + motion vector.

What some games will do is purely send the forces exerted on objects. This is okay, however can also get a bit jank when considering timing delays.

@MishMash hmm, I see about the whole server-handling. I guess the best option would be for the player to send keyboard input to the server and then the server sending the coordinates of its representation of the player character to the client to apply to its actual "playable" object, which would decrease the likelihood of high-speed cheating. I've seen in one other game where you can still move your character just fine, even if everything else is lagging and then once the lag stops, you don't get rubber-banded anywhere and server just updates your current position. This kind of a thing would require the player to have it control its own character without sending information to the server first and would make it more prone to cheating, if I'm understanding this right, correct? Well, it would be great to not have things rubber-band, but if it stops cheating, I guess there's no other way. Or, have the player not respond to key pressing if it can't update with the server, which the key-pressing one would really do. I honestly hate rubber-banding myself, worst thing ever!
I personally tend to find that whether you send position + direction, or key presses, you tend to run into desync issues with both. position + direction is more prone to desync, however it is less taxing, as you dont need to send it as often (whereas, key presses can be quick, constant and packed together). For example, if a client sends 3 key-presses, each a frame or two apart, these may end up getting pooled together and received in one go by the server. As a result the effect will be different to what the player had done.
So, either way, you'll probably need some form of rubber-banding/cheat control, as equally, a player could create a bot which sends 100 key press packets in the same frame.
Though both methods do work, and both have their strengths and weaknesses. I'd say that key-presses and both local and server processing is more robust to cheating, however equally, it is also more likely to desync (and desync means more rubber banding). Sending client position and speed to server feels better for the player, however is more prone to cheating, so you would need the server to verify how "valid" a move seemed. (Simply, this can be done by just checking distance and time between the last position, and checking against the max speed. More complex systems would evaulate whether it was possible for an object to accelerate that fast.)
The server should always be able to override position, or modify velocity however.


I think the take away point here is to try things out for yourself and see what works best. A lot of gamedev isn't necessarily about following "perfect" practices, but trying things out and seeing what works for you, and what delivers both good results, is fast and easily maintainable :)! The things I mention are mostly just based on what I have tried in the past, and techniques/tricks I use in my current game.
 
Top