I took the month of November off, which had been very necessary, as I hadn’t had a proper vacation in a year. In this month, I slowly started being able to draw again, and I did a lot of drawing. I also had a conundrum: I wanted to learn a bit more about Godot Engine, but I also wanted to learn a bit more about internet technologies. Solution: Write an XMPP client in Godot.
Connection and sending data:
I started on last Thursday, with setting up the TCP connection. I first set up prosody on my computer, with it only accepting traffic from the localhost so I had something to test again. I also installed Gajim and Kaidan so I had an idea of what proper xmpp clients look like (and so I could chat with myself). Unfortunately, because of QML shenigans, Kaidan doesn’t start.
For the stream itself, I am very indebted to this little project, as it showed me that you can use the process function in Godot to periodically listen to the TCP stream, which was a connection I hadn’t been able to make from the documentation. After I got that done, I still had some trouble getting the server to accept my data… turns out Godot’s convenience functions for strings and others helpfully prefixes it with a few bytes to indicate the size of the sent data. Using
put_data(string.to_utf8()) for this solved everything. Now the server was accepting my stream!
I also came across one annoying limitation in Godot: I can do host resolving, but I cannot seem to do a lookup on DNS SRV, which is used in the XMPP spec to point at the precise IP address and port to use for an XMPP Client. This means that right now the add-account button requires you type in the IP address and port of the server manually, which is really annoying.
The next day it was time to negotiate with the server on features I wanted to use. Or, mostly to get through the required TSL and Authentication services.
Godot doesn’t have a XML writer, but thankfully it does have an XML parser, which is similar to the XMLStreamReader you see in Qt. This is useful, because it can be used directly for parsing incomplete XML, like XMPP might do over the stream.
The way these kind of parser should be used is that you read the next line, and whenever you come across a new element that is an opening tag, you pass the parser into a specialised function for that element, and parse until you see the closing tag, in which case you return.
I am not doing this everywhere in the code, but it is the simplest way to use this, and I honestly should be doing it like this everywhere.
So, setting up an SSL connection was either suspiciously easy, or I messed it up. Basically, Godot just has you make an StreamPeerSSL, which can wrap around any other stream peer (such as the one handling our TCP connection). The thing that still confuses me is how to check the ssl connection. Does the client require the certificates? If so, how do you get the certificates? Or is this all handled by the StreamPeerSSL? The documentation for this is very sparse, so I am completely confused on what has exactly been established.
Prosody seemed to be okay with it, but the whole connection was over localhost, so I am unsure how much trust I can put in that…
SASL and SCRAM
Next up was SASL. I had hoped to be able to do anything better than PLAIN, but even though Godot has support for hashing Sha1 and sha-256, there’s no exposed HMAC functionality, which means SCRAM is not possible. HMAC functionality seems to be in the works, though.
Anyway, I was pretty pleased once I got authentication going, though I still couldn’t do much: Resource Binding was still required by the server.
I think info/query handling gave me the most trouble. The brunt of XMPP messages are the stanzas, which can only be sent after stream negotiation is done. Stanzas have subtypes of message, presence and info/query, or ‘iq’. Resource binding, which is basically telling the server which device is using the account right now, needs to be done to complete stream negotiations. Resource binding is an iq stanza… After recovering from that paradox, there is the next problem.
You can send out message and presence stanzas into the void and not worry about them after that, but iq stanzas always need to be replied to. Those replies can come in at any time, and after the reply has arrived, stuff needs to be done with it, like error handling, or sending the information to whatever object requested it. A reply is recognized by having it have the same ID as the initial iq stanza. So I needed to keep a list of queries, that also connected to whatever class required the query to begin with, I also needed to make sure the object gets freed once I am done with it, because otherwise it could clog up memory, oh, and I needed to ensure it could handle error cases.
I spent two days on trying to figure out how to handle this properly, making my head spin. I didn’t want to use signals for handling when the query was done and could be removed, because I myself have a hard time tracking what signals are connected to a given object at a time. It seemed to feel like a multi-threading problem, where you’re not sure whether a thread is done processing. So I decided to solve it like one, treating the queries as jobs, and creating a query processor, that checks each query periodically (through the _process() function), sends stanzas if necessary, ensures incoming replies get forwarded to the appropriate query, and removes queries when they’re done.
To ensure the queries get freed, I went ahead and made the query class inherit Resource, which is a class that frees itself when there’s no references to it anymore. Then whenever I make a query, I connect it’s ‘finished’ signal to the object that needs it with a one-shot connection, so the connection is disconnected once fired, and then the query processor can just remove the query from it’s list when done.
Message and Presence
The rest of Sunday, I ended up spending most my time writing up classes for presence and message. I eventually made a stanza resource type, then made query inherit that, and have presence and message inherit that too. Mostly just a lot of spec-reading and typing work.
I was also baking speculaas. I burned 18 out of 63 cookies and burned 0 out of 41 spice nuts. This was largely because of an error in the spec of the cookies — they should’ve gone only 10 minutes into the oven instead of the suggested 20.
Said speculaas was because the fifth of December is Saint Nicholas day, and I wanted to sent my family something nice, given we can’t travel at the moment. This was well received on the days afterwards.
Most of the days after that was spent on decorating and sending the cookies, and getting back into the swing of things regarding Krita, as my vacation was over. After all that I ended up being really tired, so I did mostly a bit of gui work.
One of the more annoying differences between working with GUI in Godot and working with GUI in Qt, is that with the latter I can assign an object name to a widget in designer, and then move it around and not worry about my code breaking. In Godot, however, nodes need to be manually pathed to inside the code. You can set a reference to it, but everytime you change the layout, for example putting a button inside a HBox, the path changes and you have to go into the script to fix that. There’s a ‘find_node’ function, which makes Godot search the named node for you, but the documentation explicitly says it is not recommended because it might be slow.
Other than that, when I was handling the roster items, I kind of missed the model system you can find in Qt, which might seem incredible. It’s just, it took care of so much handling of updates that now need to be manually handled. Did have some fun writing a custom drawing function for the Tree node that contains all the roster items, which was as easy as writing the function and telling the roster item that it should use a custom drawing function and to use the function I tell it to.
I put in avatars and date-time stamps inside the GUI even though there’s no XEPs implemented to handle either, I just felt like I should have good place holders to dress it up a bit. When it came to handling rosters, I went ahead and read some XEPs to see if the way I wanted to setup the architecture was how coherent with what spec-writers seem to think it should be.
I also came across a weird issue where if I connected multiple accounts to my localhost prosody server, and stuff would come in, Godot would freeze. This is still happening, and I am not sure why. Given that Gajim can handle this same situation I am inclined to say it’s a bug in Godot, but I wrote my first TCP connection a week ago, so I might be doing something obvious wrong…
Finally I cleaned up all the code and tried to make everything more coherent and documented.
Feedback on Godot:
A list of the things I noticed about Godot, summarized for convenience.
- I wish the documentation had more code examples on how a given node is to be used. The tree node has this, and it clarifies a lot how a given feature is to be used. Similarly, I ended up searching around a lot which GUI element was the one that I needed, so something like the page for layouts, but with all the standard gui elements would be nice.
- The example of the TCP stream above would make a really good sample for the StreamPeer pages, like, giving users the idea they could use the process function to listen for incoming messages.
- StreamPeerSSL could use a bit more explanation of what guarantees it gives. Overal a bit more explanation over how SSL works, and which parts Godot takes care of.
- As mentioned, there are some network features missing. DNS SRV, I can imagine, might not be the most important feature out there, but HMAC and other crypto options are kinda really important, even with TLS. HMAC seems in the works though.
- GDScript isn’t hard to learn, most of the learning I had to do seemed to be API specific, which I was going to have to learn anyway?
- The Resource type is really nice, and it gave me a lot of reassurance that I could just juggle around a lot of data and have minimal leakage.
- The constant need to change the node path for scripts when a change has occurred in the scene tree is really annoying, especially in the early parts when you’re still trying to figure out what should go where.
I put the code up on invent.kde.org. I haven’t actually tried to use it with a non-testing account, but I think that despite it’s issues and limitations it should still prove to be a useful example for how to write a relatively low-level networked program inside Godot. If I can find a way to get rid of the limitations, then I think it’ll be fun to keep working on it.
Anyway, I hope you enjoyed my rambling report of my excursion into the world of XMPP in Godot.