Godot Adventure - Just Follow The Documentation!?
A story about my experience trying to write a multiplayer game with Godot.
One evening, a video about UDP hole punching comes up in my YouTube recommendation. After watching the video, I remembered how old games sometimes doesn't work with peer to peer mode. The game sometimes told us to use port forwarding to let the other players join. Back then, I was just a child that want to play games and I didn't understand any networking. I think the game tried to use UPnP and fallback to asking user to do a port forwarding if the UPnP failed. I really liked old games like that since it is timeless. Even after the game is no longer being developed and has no official server, as long the games run on your PC, you could still play it with friends.
I have been wanting for a long time to create a game. Godot, being an open source game engine with cross-platform support, seems like a good solution. My initial goal was to explore its multiplayer capabilities, which seems easy enough to use. I decided to start by extending a demo project from their asset library. I found this chess project that is really simple and fit my need. Exporting the project directly also works, so now I just need to implement the multiplayer function. Following the UDP hole punching YouTube video, I still need a registry server to let the players share their IPs and ports. Since my goal to learn Godot, why don't I use it as a registry server and maybe connect it to Redis to saves all the IPs and ports.
One thing that I noticed reading Godot's high-level multiplayer documentation is that the web export only supports WebRTC and Websockets. I'm not familiar with WebRTC, but a quick google tells me that WebRTC uses UDP, which is perfect for my use case. Additionally it seems that WebRTC already implement a similar method to UDP hole punching using a STUN Server. However, since my goal is to learn multiplayer networking in Godot, I wanted to use both WebRTC and Websockets. So, for now I will use Websockets, a technology I was already familiar with. I also want to see how bad is TCP compared to UDP for multiplayer game in Godot.
It seems that their Websockets documentation is outdated, WebSocketServer
returns a error for me. One comment suggests to look at examples from GitHub instead. Looking at the chat example, it seems that it is now called WebSocketPeer
for the client and TCPServer
for the server. However, in multiplayer example they use WebSocketMultiplayerPeer
instead. As a new Godot user, this was pretty confusing. After some testing, I assume that WebSocketPeer
is used to create a generic Websocket connection, while WebSocketMultiplayerPeer
is used for Godot's high-level multiplayer abstraction.
[...] Most notably, the HTML5 platform currently offers WebSockets and WebRTC support but lacks some of the higher-level features, as well as raw access to low-level protocols like TCP and UDP.
I have created a simple server-client code using Websockets. My code works locally so now I just need to test it remotely. Godot has an export for dedicated server, which is explained in their documentation. I use the following example to start my dedicated server export.
if "--server" in OS.get_cmdline_user_args():
# Run your server startup code here...
#
# Using this check, you can start a dedicated server by running
# a Godot binary (editor or export template) with the `--server`
# command-line argument.
pass
To my surprise, my dedicated server does not work. Since it worked locally, I thought that the problem must be with the network. I use NGINX as a reverse proxy, so I spent considerable time trying out different NGINX configuration. No matter what I tried, the connection still failed. So I started to suspect my Godot instance instead. I checked the used port on my machine with netstat -tulpn
and there was no port for Godot on the list. It seems that the problem is within my Godot instance. I suspected an issue with the Linux export, as my local Windows setup worked fine. To debug this I just use print statement on the _ready
function. After a while I found the culprit was that the --server
argument that I passed was not recognized and the Godot instance didn't run in the dedicated server mode.
Godot script
func _ready():
print("Starting game.")
print(OS.get_cmdline_args())
print(OS.get_cmdline_user_args())
Output
$ ./test.x86_64 --server
Godot Engine v4.4.stable.official.4c311cbee - https://godotengine.org
Starting game.
["--server"]
[]
I need to use OS.get_cmdline_args()
instead of OS.get_cmdline_user_args()
to detect the argument. This was hard to debug, since I never expected the example on the documentation to be wrong. Maybe it is a bug, maybe the documentation is outdated, or maybe this only happens in my machine. So, I created an issue tracker on the Godot's Documentation GitHub about this and let's see if we can clarify what happened.
Update (24.03.2025)
After reading the documentation about the command line arguments and see how it is implemented in the main.cpp
file, I understand that I need to pass either --
or ++
first, before I can pass user arguments. So the following command line works.
$ ./test.x86_64 -- --server
Godot Engine v4.4.stable.official.4c311cbee - https://godotengine.org
Starting game.
[]
["--server"]
Member discussion