Has anyone ever perfected a method of making a multiplayer game (Local or LAN)? I’ve been trying to set one up for ages, but they just don’t look right.
People didn’t perfect the method to make games yet, so multiplayer is a different story in itself
But, things can be done, just search for Blender Game Engine Mulitplayer on Google and you should find some things, maybe you won’t have access to the source code, but it would be a bit hard to extract anyway because most of the time the thing is just hacked together very quick.
My two cents if you went to properly go at it:
Find/Create abstractions and never directly manipulate sockets:
socketmodule in Python is one of the lowest level API possible, its almost like writing C++ at this point… But it is also all you have. (unless you play with
pipand install neat libraries, but then the problem becomes running it inside the BGE…) So, build classes and a more convenient API on top of these bare sockets, don’t just send stuff like a madman. It might also be handy when you will make your own protocols on top of UDP/TCP.
UDP or TCP are not enough alone.
Sure, you will have to pick one of those two protocols as a base for your communications, but here are some pros and cons about it:
- Message based (get exactly what you sent, or get nothing at all).
- No waiting, send the packet and forget about it.
- Packet limit size (you have to manage that yourself).
- Because it is fire and forget, packets can get lost, depending on your use case it might be a problem that needs to be resolved.
- Packet ordering: a message sent later can arrive before messages sent sooner, you might want to prevent this yourself too.
- Packet size is managed for you, just send stuff, TCP will split it for you.
- Send things, receive exactly that on the other side, in order, no loss.
- Multiplexing multiple connections through one socket (one server, multiple clients with specific socket handles)
- Stream based, what you send might be cut in parts without you knowing how exactly. Yes everything will arrive on the remote host, but you cannot assume that you can send unique messages, everything is put end to end into one continuous stream. You have to start thinking how to segment your stream.
- Connections can be hard to manage properly.
- Because everything is ensured to arrive in the right order, your whole communication will be delayed until everything that needs to be sent/received is properly acknowledged, TCP will make the rest of your data wait until the stuff before is correctly transmitted.
So if you want some messages to be received no matter what, you can send it through TCP, but as TCP is stream-based, you will have to take care of having a way to handle that stream.
# example tcp: tcp_client_to_server.send('a') tcp_client_to_server.send('b') data = tcp_server_to_client.recv(1024) assert data == 'ab' # tcp merges everything into one stream # example udp: udp_client_to_server.send('a') udp_client_to_server.send('b') data = udp_server_to_client.recv(1024) assert data == 'a' # udp outputs packet after packet, no merging data = udp_server_to_client.recv(1024) assert data == 'b' # second packet received
Note: The order in the UDP example might be reversed depending on the network’s conditions, or the messages could not even reach the target.
I strongly recommend implementing your own protocol on top of these two, based on your exact use case.
Find a way to poll for events on your sockets.
In the BGE, you are not in control of the mainloop, you are just part of one iteration of said mainloop. You are not allowed to block, else the BGE will wait for you before rendering the next frame. You then need a way to query your networking objects to process events only if there is something to do, else just wait until the next frame.
For this I will never recommend the
selectorsmodule enough. It allows you to register multiple sockets to it, and just do
selector_instance.select()which will return once at least one socket is ready.
In my opinion, this is the best way to listen for network events without starting a new thread for each socket to do
.recv(...)on it (because
recvis blocking by default, you can make it non-blocking, but then you need to do try/except in order to catch timeout errors).
The alternative is having a thread running in the background (that could still use a selector to listen for events on the sockets), but the problem with threads in Python is that you should avoid creating too many of them, and you also need to make them stop properly because they could be stuck and persist in your editor if not careful.
Another solution is to start a new process that will listen to the network, and pipe the events via some protocol from subprocess->bge_process, but it still isn’t something easily done if you don’t know what you are doing.
With a friend of mine, we did try to put up a framework for the BGE that would also pack some networking capabilities. One of our game prototype using said framework can be downloaded here:
bgez-network-test1.zip (372.9 KB)
requires UPBGE 0.2.4
There is a README explaining how to start the server or the client from the same
TL;DR: Efficient networking in the BGE is yet to be figured out.
Here’s what I wonder: how does one go from sending packets, to discerning between users? It boggles my mind. Can one, for instance, toggle an object’s visibility, and have this affect only one player? I really need to look for some examples on this.
See the zip I sent, where each player is managed independently.
To expand a bit on the subject:
In TCP, each time you create a connection, you have a new tuple
(ipA, portA, ipB, portB) which represent each socket on each side of the wire. When doing HTTP over TCP, the server assumes that a new client exists each time a new connection is made on its listening server socket. Even though the client’s IP might be same.
Example: Open 5 tabs on the same website, this opens 5 connections, from your own IP, but different ports will be used. Same IP, different ports (on your machine). The server on the other hand still has the same endpoint.
(yourIP, port1, serverIP, serverPort)
(yourIP, port2, serverIP, serverPort)
(yourIP, port3, serverIP, serverPort)
(yourIP, port4, serverIP, serverPort)
(yourIP, port5, serverIP, serverPort)
So just from that port difference in your tuple, you know that it is a different connection. You can sometime assume that each connection is a separate client.
But if you want to make an application using multiple connections per clients, then you need a way to regroup them and identify who is at the origin of each connection (to pack each connection under the same client on your server). For this you usually need to have a protocol allowing you to send some metadata to identify the person talking with you, such as an ID of some sort.
For making games, you would probably make your own protocol based on TCP/UDP where you would find a way to pass around such an ID in order to identify everyone.
Just looking at the socket ip/port is not enough, example:
You are at home with your family. Everyone wants to use his own computer connected to the house’s wifi router, and you all connect to some game server. In this case, you will all share the exact same IP (but different ports). The server has no way of knowing how many different people are behind some IP, unless it gets the information from the messages themselves. You cannot say “but the port is different”, because one machine can use as many ports as it likes.
TL;DR: You add an ID of some sort + all the relevant info needed in each of your messages, hence defining a specifc protocol for your use case.
Very informative, thanks a bunch c:
Just my two cents: On https://gafferongames.com/ you can find some well written explanations about challenges in Multiplayer games in general. Good read!