Versions Matter. Finding Bugs and Updating CLJSJS ClojureScript PackagesWritten by Bunkers on April 5, 2017
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.
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.
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.
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.
Back to debugging! I found that I could attach to the events on the
Mouse object and they fired correctly. However the
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
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.
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.
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.
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.
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.