back home

Versions Matter. Finding Bugs and Updating CLJSJS ClojureScript Packages

Written by Bunkers on April 5, 2017

TL;DR

There's a big spoiler in the title of this post! I was developing the logo project and thought I'd discovered a bug. Turns out I was using an old version of Matter.JS and I set about upgrading the CLJS JS package.

Constraints

Last time I'd reached the point of having a world defined using a ClojureScript data structure. Running the code would have a couple of circle bodies drop in and bounce around.

To start building the structure of the logo I need to join objects together. In Matter.JS, bodies you join bodies together using constraints. I'm going to cover constraints more in another post, but they join bodies together using a kind of invisible thread, that can be hard like a metal rod or stretchy like elastic.

Similarly to my first experiments with Matter.JS, the Coding Train has a tutorial covering all the techniques needed to define and use them.

I started in the same vain as that tutorial and created a constraint to join my two circular bodies together. You could imagine how I could build up the letters of the logo this way.

In future I'll convert each letter to something Matter.JS calls a composite, but as the 'world' is a composite itself that shouldn't be a big deal. A composite is just a body that is a collection of other bodies, constraints and composites, so it creates a hierarchical structure.

Mouse Trap

My experiment was working and the two bodies fell as if attached. When they bounced it looked like a spring was joining them together and it was satisfying to watch! But I wanted to interact with it. Pick up one of the balls with my mouse and fling it around the screen.

Fortunately Matter.JS provides a MouseConstraint for just that purpose. Setting one up involves creating a Mouse object which captures all the mouse events. You pass the Mouse object to the MouseConstraint when you create it. Add the constraint to your World and you can start clicking on bodies and dragging them around.

Except mine didn't work! No matter what I did I couldn't get it to fire the right events. The MouseConstraint works by having your mouse pointer at one end of your string/rod/elastic and attaching any body you click on to the other.

Bug Trap

I started debugging it. The tools for this are excellent. Having source maps for the ClojureScript code so you can set breakpoints is fantastic. Add in the BinaryAge tools for logging objects and the tools are pretty advanced.

To being with I experimented with the pixel ratio as is a problem in the tutorial I was following. However, it seems like Processing.JS doesn't support that idea and scales the coordinate system instead. This has made me wonder further about creating a version of Quil backed by P5.js. It's fast becoming the JavaScript Processing-like library of choice, and seems to be in more active development.

Back to debugging! I found that I could attach to the events on the Mouse object and they fired correctly. However the MouseConstraint's update method, where the meat of the logic happens, never got called. A bit more digging and I could see that it wasn't called via a mouse event but an event called tick. This event gets triggered by Matter.JS's Runner each time it updates the World. I wasn't using that and manually calling update on my Engine object in my Quil sketch's update-state function.

First I tried a work around and triggered the tick event myself. Suddenly it all worked! "I've found a bug too", I thought, but then I realised that Daniel Schiffman had stopped using the Runner in the tutorial, and his worked.

Searching forums, GitHub and Stack Overflow for related issues, I came across this issue, identified as a bug and now closed. So why wasn't my code working? The user reporting the bug even posted the same workaround I was using.

I'd noticed previously that the version of Matter.JS in the CLJSJS package was old, and looking at the releases I could see a latest version of 0.10.0. The version in the package was 0.9.1 and so didn't include this fix.

External Repairs

I took a deep breath and decided I would update the package. The guide for creating new packages is well written, and made me realise that this wasn't going to be the horrendous task I first thought.

Installing boot was simple via Homebrew

brew install boot-clj

I created a fork of the repository, cloned it and started by updating the build.boot file to grab the 0.10.0 version. This was as simple as updating the version numbers, and creating a checksum by downloading the zip myself and running:

openssl md5 matter-js-0.10.0.zip

The bulk of the work was with updating the externs. I know there are tools to do the generation of this for you, but I decided to first run a diff of the two versions and see what objects and methods were new.

It turned out there wasn't that many! I added them in to the matter.ext.js file, and installed my new package. This meant I could update my package version number in my project.clj file and it would start using my new version.

Feeling proud of myself I restarted everything and found it didn't work! I'd successfully got the new version of Matter.JS included, but the events still weren't firing.

It was at this point that I discovered a little link at the top of the Matter.JS releases page on GitHub.

The hidden newer releases!

Hidden away were two more releases 0.11.0 and 0.12.0. Slightly despondent I setup about the process again, but this time using the latest version 0.12.0.

It wasn't as straightforward this time and there were a number of new objects and methods to update. A global change involving the handling of calls to require also meant that I had a lot of changes to wade through.

However, this time I updated everything and restarted my project to find I'd fixed it! It was an immensely satisfying feeling, and made playing around with those two yellow circles all the more enjoyable.

Pull Request

There's some tidying up work to do before I submit a pull request with these updates. First of all I've manually added all the new objects and methods for handling things like plugins. I've not tested any of this yet, so that would be a good start! I also made a couple of commits before fixing it, so I'll need to tidy up the history too.

In the meantime, if you want to install it yourself you can clone my fork. You will have to do things like setup boot yourself and to be honest it probably isn't worth the hassle. I'd just use the workaround which is:

(.trigger js/Matter.Events engine "tick")

It works - as long as you've got your engine object to pass in of course - so why fix it?

I'll update this post with a link to the pull request when I submit it. That way, if you're interested, you can track it. Incidentally you can just get an alert when it closes via Tell Me When It Closes. It's a useful tool that means you don't have to watch issues and get all the updates before someone fixes it.