GMS 2.3+ Anyone made a 3+ player TCP multiplayer system?

Neptune

Member
Hey guys, I'm looking for a bare-bones TCP system where one of the clients is also the host/server, and all clients (including authoritative host) are sending each other some basic info (x/y, appearance, state)

Anyone have a good system up and running? I'd love to compare notes on how things are being handled and learn, if you are willing to share code.
Any information is appreciated :)
 
Last edited:

Alice

Toolmaker of Bucuresti
Forum Staff
Moderator
Don't know much about networking in GML, but one important information would be whether you want updates roughly in real-time or on a more relaxed basis (e.g. turn-based).

If you only need to update the information every few seconds or so, or as soon as some player makes a move, TCP might suffice, I suppose?
However, if you need to react faster to the player input, UDP will be necessary. Some kind of error-avoidance/handling might be needed, too.

(in general, TCP -> slower and reliable, UDP -> faster, but might drop packets)
 

Neptune

Member
Right, I've kinda got a TCP network going (and though UDP is faster) TCP seems quite fast for simply having some avatars running around together.
I'm having a few hiccups, and I need to see someone else's. All the tutorials I've seen are people just kind of stumbling through the GM functions without really setting up a solid system :L
 

FrostyCat

Member
If your clients are passing coordinates to each other, then the host is NOT authoritative. In a setup where the host is authoritative, clients are ONLY allowed to say "I want X", only the server gets to say "Y is Z" or "Y did Z".

Quite frankly, once you learn to pass messages back and forth, there's nothing more to learn from the coding side. Everything else is grounded more in theory than code, and the specifics will vary wildly from project to project. For example, the equivalence of correspondence chess and multiplayer games:
One thing that I now demand of all networking novices is an understanding of procedures in correspondence chess, even if they don't all play chess. That's the closest physical analogue of how actual multiplayer games work, and it stops dead most rookie myths about multiplayer games.
  • The connection is not a shared board between the players on a table, it's just the mailman in a correspondence game.
  • Nothing happens on your board until you act upon mail sent out by your opponent.
  • Nothing happens on your opponent's board until you send him/her mail stating what your move is.
  • For the same reason you can't use chess notation in backgammon, bridge or StarCraft, you have to tailor the form and notation to the game.
If you would just understand that it's just a conversation, the structure of the conversation (i.e. what can be said and what to do when someone hears it) would call itself out to you, and the code is only a direct translation of that conversation template.

In your case, after the initial connection, there are only 2 messages that need to be handled:
  • ID 0: Server tells clients that there is some player at a given position with the given appearance and state. When a client receives it, it tries to find a local object instance that matches it (creating it if not existing), and updates the said properties as given by the server.
  • ID 1: Client requests that the server update one or more of its properties. When the server receives it, it would do some validation on the request, then pass an updated ID 0 message down to all clients (including the sending client).
Start with implementing these after you've got the initial connection working, and there's your 3+ TCP multiplayer system.
 

Neptune

Member
@FrostyCat Thanks. I've thought about this quite a lot, and done a lot of reading - I understand multiplayer.

@Tornado Alright will check it! Thankyou. [EDIT] This seems... like it would not yield much instruction and further complicate things.

@ricardo1996

For the networking portion (in my case where 1 client is ALSO the server - distributing the other clients' datas, as well as its own client data) there are a few things that are confusing to me:
1) How to stop the server from sending a client its own data back (I've been sending the socket ID with the data to compare the list of connected client sockets "dont redistribute Client X data back to Client X")?
2) How to manually disconnect a client?
3) How is the Async Network event running? It just constantly listens and responds as fast as possible once a server is started?
4) 'network_type_connect' does this only run on the server's async-network? Or does this happen for all clients when someone connects (disconnects etc)?

Answers to any of those questions are much appreciated 🙏
 
Last edited:

O.Stogden

Member
When server receives a packet, the "socket" is automatically stored in the ds_map for your async event, you shouldn't need to send a socket with it.

Code:
socket = ds_map_find_value(async_load,"id");
Under network_type_data in your server's async event will retrieve the socket of the client sending you the data.

I use network_destroy on obj_client's Clean Up event, which disconnects it from any server it may be connected to. The only event this fails in, is if the client crashes unexpectedly or some other situation that means the Clean Up event fails to run. In which case I can't remember if it disconnects by itself eventually, but if not, you should be running ping checks periodically to check if anyone (or if indeed the server) has lost connection.

aysnc network is activated whenever something connects, disconnects or sends data to you. Yes it effectively does "constantly run".

network_type_connect I believe only triggers in the server. Clients don't connect directly with other clients. I believe network_type_data is the only relevant type for clients, which would be to receive data from the server.
 
Last edited:

Neptune

Member
@O.Stogden I see! That makes a lot of sense, thankyou.

Ok so 'ping' would just be ensuring each client is sending data - Is there a specific way to do that. or I just manually keep track of the sockets' sending data?
 

GMWolf

aka fel666
Quite frankly, once you learn to pass messages back and forth, there's nothing more to learn from the coding side. Everything else is grounded more in theory than code, and the specifics will vary wildly from project to project.
I wouldn't say that's true.

Implementing something like rollback reconciliation for instance will require a lot of complex code.

Networking is a vast and complex subject.
theres interest management, packet compression, dead reckoning, etc etc.
It's not a simple as having "a conversation" and sending packets.

1) How to stop the server from sending a client its own data back (I've been sending the socket ID with the data to compare the list of connected client sockets "dont redistribute Client X data back to Client X")?
The server shouldn't be in the list of clients to send updates to.

There are usually two ways to structure games:
Either you run both a server and a client. The client acts like any other client connected to the server. (You can do this by having two sets of in instances, some for the client, others for the server).

Or you have a special client that also acts as a server.

In the first case: it should work itself out.
I'm the second case, the server would have a list of clients to send data to. It wouldn't be in its own list.

2) How to manually disconnect a client?
You can send a message with the disconnect ID.
It's also a good idea to send keepalive packets. If the server doesn't receive a keepalive packet after for some time, it can assume the client got disconnected.

3) How is the Async Network event running? It just constantly listens and responds as fast as possible once a server is started?
There is a separate thread that waits on a network socket. When it receives data, it puts it on a queue.
Every frame, GM goes through that queue and creates async events. (Which means the async event is I'm fact, quite synchronous).
 

FrostyCat

Member
I wouldn't say that's true.

Implementing something like rollback reconciliation for instance will require a lot of complex code.

Networking is a vast and complex subject.
theres interest management, packet compression, dead reckoning, etc etc.
It's not a simple as having "a conversation" and sending packets.
Rollback reconciliation is not unique to multiplayer. Any 1P game that has an undo/redo feature must also implement rollback in some form. While I agree it can be complex and relatively incompatible with mainstream ways of using GML, it is not a networking-specific issue.

Load balancing, interest management, packet compression and dead reckoning are really just alternative ways to speak in order to maximize a given objective --- availability, bandwidth efficiency, etc. In the end it's still a matter of conversations and sending packets, only the specific actions are different. Nobody who still treats a connection as a shared board between two players is in any position to start on these additional topics.
 

Neptune

Member
@FrostyCat "Nobody who still treats a connection as a shared board between two players"
I'm confused... You're saying this is what I'm doing? There are no client to client interactions in what I'm trying to do (other than the host, who is also a client)... A client / server
 
Last edited:

Neptune

Member
@FoxyOfJungle Hey! I'm wanting someone from US and Europe to connect up, and send eachother "Hello, World!"

And I was thinking this would allow for that, by connecting to the other's Ipv4 (192.168.x.xxx) kind of thing -- am I wrong in thinking this?
GML:
global.server = network_create_server(network_socket_tcp,global.server_port,global.server_clients);
 

Padouk

Member
@Neptune
That's for running deployment on Steam right?
You will start having trouble with the GML base network_* as soon as you will try to connect with the outside world

Most players run behind Home Route (aka NAT) where their private Ip are not exposed to the world. your 192.168.*** won't be reachable from outside your house
 

FoxyOfJungle

Kazan Games
@FoxyOfJungle Hey! I'm wanting someone from US and Europe to connect up, and send eachother "Hello, World!"

And I was thinking this would allow for that, by connecting to the other's Ipv4 (192.168.x.xxx) kind of thing -- am I wrong in thinking this?
GML:
global.server = network_create_server(network_socket_tcp,global.server_port,global.server_clients);
Calm, 192.168.XXX is usually a local IP, for what you want to do, you will have to rent / buy a VPN host and host the server there, however I recommend doing this using another language, like Python or Node.js. (The graphical part of the application makes it slower, so I recommend not using GMS 2 to create the server, only the client).

Use network_connect_raw(socket, url, port) to be able to connect to a server made in another language.
 

FrostyCat

Member
@FrostyCat "Nobody who still treats a connection as a shared board between two players"
I'm confused... You're saying this is what I'm doing? There are no client to client interactions in what I'm trying to do (other than the host, who is also a client).
I made that response to GMWolf when he claimed that it's not just about conversations. I didn't mean that you are one, but anyone who is needs to get that sorted out first.

Whether you can make a multiplayer game work, or implement any of the enhancements GMWolf described, depends entirely on whether you can see through the conversation that goes on behind the scenes. When you do, not only are you in a better position to implement them, but they will sound perfectly natural to you.
And I was thinking this would allow for that, by connecting to the other's Ipv4 (192.168.x.xxx) kind of thing -- am I wrong in thinking this?
Anyone who thinks that 192.168.xxx.xxx addresses work across continents needs to catch up on basic network admin, which is a prerequisite to networked game development.

Here I do mean you're one.
 

Neptune

Member
Well ****.

Alright, so I DO have to host this puppy. That's a bit confusing - one of the client's is also the server, but the server has to be hosted??

@FoxyOfJungle What did you mean "The graphical part makes it slower" - just all the excess GM engine code bogs it down a bit?
I'm not looking for lightning speeds, so I don't mind if it's a tad slower to use GM.
 

Padouk

Member
@Nepute
From my understanding, your game is a Story Mode (tell me if I'm wrong here as some of my answers depends on it.)
You pretty much have a main Hero running the Story. A few Accomplice / pets can join from time to time and you would like those to be played by friends. (Am I right?)

In other word. You expect 1 player to play solo most of the time, save his progress in the story to his computer. You are now looking for a way to let your a friend jump in and play along from time to time. But the overall saved progress still belong to the main Player. (Am I right?)

This takes a lot of guess works out of your hands.
- Don't worry too much about state persistency and other stuff. Your Main Game (the hero's Computer) is authoritive. He tells the story. He runs the story. If your Main Game crashes, your friends will join back in to your last saved point.
- Don't worry too much about Creating Rooms and Lobbies for wait periods or broadcasting your presence.
- I would (as you've probably done) Simply implement the remote clients as "Remote control" sending their actions to the Hero's Host, waiting for a "World Update".

I know you are planning a release on Steam, I would advice against using plain sockets for that matter. (they are great for development, but not for nothing else really)
- I would recommend you to setup a Steam *MatchMaking* layer that create a Private Lobby with "FriendOnly" level right when the your game starts. That way all your friends will see the "Join this game" button in there steam friends listing.
This way your Friends can join in from the Steam Friends chat to play one of the accomplice/pets.

- I would also recommend you to use the Steam P2P relay to reroute the traffics instead of using the base GML network_* functions. (The idea here is to use Valve' network (or any other relay really.. like photon networking) Instead of writing to a tcp socket.. you write to their wrapper.

- I would recommend you the simple Client/Server approach where the Server (your Hero game) Broadcast the World state to the clients every 15-20ms. And the clients simply update all their obj accordingly. That's what you have already done if I've followed correctly.
- For your first draft, I would recommend your Client to do absolutly nothing else than sending the Key pressed to the Server.
- You will need a second draft where you interpolate to hide that 200ms-400ms ping between the click and move. Since you appears fairly new to Networking I would recommend you to save that as a "later deal" optimisation.

---

I know those 2 concepts (Steam MatchMaking and Steam P2P) are not well exposed to GML, so you will need to go some other way.
I can't advocate my own dll here since they are all private and propriatary bound... But searching the community I know YellowAfterLife is maintaining a great wrapper. https://yellowafterlife.itch.io/steamworks-gml-example
 

Neptune

Member
@Padouk This has very little to do with my game. Until I can do a simple data transfer, it is far from being implemented with Steam or my game.
And my game is never going to be multiplayer.

This is what I'm hoping to accomplish:
1603057406253.png

I'm wanting any user with an internet connection to be able to become a client/server and invite other clients to join.
I thought this was the whole point and advantage of a client/server... Is that you dont need dedicated servers or online server hosts?

@YellowAfterlife Would you share your thoughts on this please? 🙏
 
Last edited:

FoxyOfJungle

Kazan Games
@FoxyOfJungle What did you mean "The graphical part makes it slower" - just all the excess GM engine code bogs it down a bit?
I'm not looking for lightning speeds, so I don't mind if it's a tad slower to use GM.
Only the fact that GMS 2 generates graphics (surfaces, these things...) in the game slows it down, and a more "pure" language will make it faster, like the ones I mentioned.
But it doesn't stop you from doing it, it's just a fact.
 

Padouk

Member
Yeah That's what I ment. You need a relay for that.

Think of a relay like a proxy. It's sole purpose is to receive and foward traffic on your behalf.
It exists and does nothing else than consume bandwidth.
All "Games clients" send their "packets" to the relay and the relay redistribute or broadcast them. You still need a Server to run it.
1603057631962.png


This is required to overcome most of the Home Route and Firewall issues.
In my previous comment I was talking about Steam Networking since it's the choice that make most sense for a steam deployment. (https://partner.steamgames.com/doc/features/multiplayer/networking)

They are slightly harder to come by in GML if you are not into C++ writing. But like I said, others from the community Have provided some help here.
 

Tornado

Member
@Padouk This has very little to do with my game. Until I can do a simple data transfer, it is far from being implemented with Steam or my game.
I'm wanting any user with an internet connection to be able to become a client/server and invite other clients to join.
What I learned until now is if you want peer-to-peer communication (like in your drawing), which goes beyond the boundaries of LAN, you will need UDP hole punching. Check here the info about p2p, NATs and hole punching.
https://bford.info/pub/net/p2pnat/
But for that you will need a server which performs negotiation beetween the peers as they can't do this alone if they are behind NAT(s).

Or if you don't do P2P, you'll need again a hosted server. Then all clients communicate over that central server.
(I'm in a process of collecting information right now, I'll start soon with P2P.)
 

Neptune

Member
If I don't care about speed, and I want the slowest/worst international multiplayer connection there ever was, can this be done using GM alone? A client/server/relay(?)...
 
Last edited:

Padouk

Member
No, no Relay is not for speed.
It really is for Public Availability. (Private vs Public network).

You wouldn't need a relay if you had a dedicated host since that host would have a Public IP for your network_connect.
In your case you have a bit more issues to deal with.


Can it be done using GM alone is a rough question. Short answer would be no, Longue answer would be yes.

Short Answer:
For network_connect to connect to a network_create_server. The IP must be visible. (192.168.*.* is not visible over the internet). so GML only allows connection to public ip.

Longer Answer:
To host the Server and Client into someone's computer (running in a private network) You are left with 3 choices:
1) [No No] Open up your computer to the public world (and ask everyone else to do the same) by configuring your Home Route with port forwarding
2) [higher maintenance] Host a relay (your own nodejs, php, perl, python or whatever you want hosted on a public server...)
3) [lower maintenance] Rent a relay (steam networking, p2p, photonnetwork, etc..)

That way the relay is always visible to public and GML's network_connect can always be used


Can it be done using GM alone is a rough question. My answer is yes.. been there done that... but it requires some extra work outside of GML.
if you host your own relay,
you can probably talk to it with network_connect...
Then again, most of your work would be creating and hosting the relay.

if you rent a relay,
most of them provide C++, HTML5, Android, iOS libraries to tackle the Authentification.
most of them impose restriction and requires authenticated users and app security so you would need GM extension to wrap those api..
most of your work would be in creating that extension.


For Indie developer:
Steam was built around that idea and comes built in with all the requirement to relay your paquet through the public Valve network. A few member of the community created GML extensions so you don't have to worry about that technicality and you can use them in pure GML... you only have a new set of GML functions to learn.
That would be the way for you to go.


It seems more intimidating than it really is.
 

Neptune

Member
@Padouk Ooo well I like the sounds of that. So Steam can be the middle man...
So the clients send their data to "valve network", and "valve network" forwards to client/server, and then back.
 

duran can

Member
What exactly do you want to learn?

* server setup
* connecting client to server
* sending data from client to server
* sending data from the server to all clients
* player login / registration (on server side)
* ownership control in client (like is_mine in unity photon)
* 3rd party objects synchronized on all clients (I'm still working on this ...) like boxes and monsters
* ..etc

I do not promise, but maybe I can share the construction stages of my game with my bad English like a blog by explaining the codes. :rolleyes:
 

Neptune

Member
@duran can This is what I want to do, and it seems like GM on its own is not capable.

If I'm understanding, one way or another, something must be hosted by a personal computer, online hosting service, or Steam.

In my case, where one user is a client/server it seems I need a "relay" - which makes a lot of sense to me... To simply relay information over the internet, since the clients are not all on the same network.
1603068782679.png
 

Padouk

Member
@Neptune
Crawl, Walk Run like they say!

Crawl.. you've already done that
network_connect and network_send_package
So good job here.. You already know how to buffer up the world and broadcast it.
You already know you have two choice: Server/Client (where 1 host update the world and broadcast it... best for most cases) or Peer 2 Peer (where each host update the world on their own.. really just used in Real Time strategy games anymore due to High Entity Count)

Walk.. Have a look at YellowAfterLife's wrapper listed above (sample is here https://yellowafterlife.itch.io/steamworks-gml-example)
Well.. I don't want to over advertise someone else's work, but you are specifically asking to stay in GML and he proposes a well built wrapper to expose the steam concepts as-is.

So you already know how to send packages to ip. In the Steam univers, you send packages to SteamID (the player's id actually)
You can see some equivalency in his wrapper here: https://yal.cc/r/17/steamworks-gml/#steam_net
for example you would replace network_send_package with steam_net_packet_send(steam_id, buffer, size, type)


The other question you will have is How the hell do I get the SteamID of my other players?!
For the answer it all boils down to MatchMaking where you have to create or join room to expose your SteamID to team mates.
(https://yal.cc/r/17/steamworks-gml/#steam_lobby_list)


Run..
What ever comes up your mind buddd!
Steam is one way, PhotonNetwork is another.
Regardless of the way you broadcast your packages you will still face other issues to have fun with. Interpolation, Delayed action, Authentication etc. I'd be glad to share more here and there but i'll let you walk first.
Any additionnal answers coming from me will either be in c++ or in a 10 page PowerPoint presentation :p
 

Neptune

Member
@Padouk Alright, I really appreciate all the info! Just to clarify - YAL's steamworks-gml dealio is something he likely created in a different language, like C++?

After your response, I'm getting the feeling there are very few real online multiplayer games made with GM... Seems like quite the hurdle.
But this library by YAL seems pretty awesome, and I'm definitely gonna dive into it!
 
Last edited:

Padouk

Member
No real online multiplayer games made with GM.
GM is all suited for connecting to a Dedicated Server. It's more than suitable for running the client in Android, iOS, Html5, osx32, linux64, win32, xbox... and hook up to a server with the base network_connect.
That is the typical way and GM address it.


What you are trying to achieve is called Listening Server. It's tricky on Desktop and close to impossible on Android, iOS and HTML5, xbox...
It's probably not in the high priority list of YoYo. No worries mate! They allows for extensions so we can prioritise it if we want.


Don't blame it on GM ;) You would have the same issue with Unity, Gamebryo or whatever. Your issue is the security aspect of Dedicated server vs Player hosted game. The hosting players want to be protected against black hat idiot who could abuse of their open port for uploading taylor swift meme on their behalf. For Unity, you would also have to use an extension made from the community (in this case a semi professionnel community)


I'm with you tough.. I do believe a better Network Abstraction Layer could be handy in some cases. I just don't think it should come from YoYo...

It's more intimidating than it really is...
 
Top