I am a software architect at CCP Games. Work on the launcher, Wine support, graphics engine and other random stuff for EVE Online, usually low level. This blog details some interesting things I run into at work, as well as new things I'm trying out in my spare time. Lately I've been picking up new programming languages - Erlang/Elixir, Go, Swift whereas most of my work in the past is in C++ and Python.

Subscribe to this blog

Follow by Email

Search This Blog

Go, bots, go!

Earlier this year I started experimenting with the Xmppprotocol, and implemented bots in Python to communicate with an Xmpp server. I've now revisited those bots and reimplemented them in Go. I've been meaning to learn Go for quite a while, and this seemed like a reasonable first project to tackle.

If you are an experienced Go developer, I would appreciate any feedback and suggestions on how to improve the code - if you are just starting out with Go like myself, I hope this blog and the code is useful to you.

As before, I'm using Prosody to communicate with. Just like I did in Python, my first experiment is to simply open up a socket and poke the server to see where that takes us:

This is the start of the handshake of Xmpp - I won't go into the details here as I've already covered it in the context of Python, but this is a good start.

In my blog post on the Echobot in Python I discussed how to handle the incoming Xmpp stanzas. I'm basically using the same approach in Go, although I have to resort to some tricks to handle the incomplete XML we're bound to get when feeding the incoming stream.

Just like in my Python implementation the XmppContentHandler responds to the incoming parser events and builds a queue of XmppElement objects. The XmppHandler pulls from this queue, allowing it to work on a somewhat higher level, with whole XMPP stanzas.

The Parse method of XmppContentHandler uses the xml Decoder. It pulls tokens from the parser until an error is encountered. The handler keeps track of any leftovers, combining them with incoming data so eventually it becomes a whole stanza that then gets added to the queue.

Note that it is important that the main thread doesn't exit prematurely - all the work is done in the receive Go routine. The code above sets up a channel and waits on it, until you hit Ctrl-C. This allows the receive to simply block on the Read call, then process data as it comes in. The echo bot is purely reactive so it only needs that one Go routine to do its thing.

Try this out with some XMPP chat client - I've been using Swift. Start a chat with echobot@localhost and you should see everything you say echoed back.

JumperBot

The JumperBot jumps between chat rooms, saying a few random phrases in each room, then jumping to the next one. This can be useful for generating traffic for load testing your chat server, by running any number of those bots. Seehere for a discussion of my Python implementation of this.

This time we're using more Go routines - the Run method is called as a Go routine as the JumperBot is proactive, rather than simply reactive as the EchoBot was.

Trying it out

The bots do their chatter in rooms named bot_room_0 through bot_room_9. Connect to the server with Swift (or your favorite chat client) and join one or more of those rooms to listen in.

What's next?

I need to polish the XmppHandler - the handleResponse method is kind of quick and dirty. The state machine for the handshake is simply a series of if statements and doesn't deal with errors at all. I would also like to see if there is a better way to handle incomplete XML handling in XmppContentHandler. Hopefully some experienced Go programmers out there will give me some constructive feedback on all this.

Then I want to take a step back and compare the Go and Python implementations. My first impression of the concurrent features of Go are very positive. This feels easier and more natural in Go than using Python 3 and asyncio.

Finally, I want to do some performance comparisons between Python and Go. How many bots can I run on one machine before things start breaking down?

Get link

Facebook

Twitter

Pinterest

Google+

Email

Other Apps

Labels

Popular posts from this blog

I want to describe my first iteration of exsim, the core server for the large scale simulation I described in my last blog post.
A Listener module opens a socket for listening to incoming connections. Once a connection is made, a process is spawned for handling the login and the listener continues listening for new connections.
Once logged in, a Player is created, and a Solarsystem is started (if it hasn't already). The solar system also starts a PhysicsProxy, and the player starts a Ship. These are all GenServer processes.
The source for this is up on GitHub: https://github.com/snorristurluson/exsim Player
The player takes ownership of the TCP connection and handles communication with the game client (or bot). Incoming messages are parsed in handle_info/2 and handled by the player or routed to the ship, as appropriate.
The player creates the ship in its init/1 function.
The state for the player holds the ship and the name of the player. Ship
The ship holds the state of the ship - …

Learning new things is important for every developer. I've mentioned this before, and in the spirit of doing just that, I've started a somewhat ambitious project.

I want to do a large-scale simulation, using Elixir and Go, coupled with a physics simulation in C++. I've never done anything in Elixir before, and only played a little bit with Go, but I figure, how hard can it be?

Exsim
I've dubbed this project exsim - it's a simulation done in Elixir. Someday I'll think about a more catchy name - for now I'm just focusing on the technical bits. Here's an overview of the system as I see it today:

exsim sits at the heart of it - this is the main server, implemented in Elixir. exsim-physics is the physics simulation. It is implemented in C++, using the Bullet physics library. exsim-physics-viewer is a simple viewer for the state of the physics simulation, written in Go. exsim-bot is a bot for testing exsim, written in Go. exsim-client is the game client, for inter…

I've added search and trim to my expiring records module in Erlang. This started out as an in-memory key/value store, that I then migrated over to using Mnesia and eventually to a replicated Mnesia table.
The fetch/1 function is already doing a simple query, with match_object. Result=mnesia:match_object(expiring_records, #record{key=Key, value='_', expires_at='_'}, read)
The three parameters there are the name of the table - expiring_records, the matching pattern and the lock type (read lock).
The fetch/1 function looks up the key as it was added to the table with store/3. If the key is a tuple, we can also do a partial match: Result=mnesia:match_object(expiring_records, #record{key= {'_', "bongo"}, value='_', expires_at='_'}, read)
I've added a search/1 function the module that takes in a matching pattern and returns a list of items where the key matches the pattern. Here's the test for the search/1 function: search_partial_…