Lookahead Dreamdust

I look out the window and gaze upon a washed out grass knoll sits within a white space. Sitting outside the knoll is a tree rooted in the empty space, all the roots, normally concealed by the grass are visible and winding as if it were constrained dirt.

My chest is still pumping as I rapidly sit down against the tree. “Wait. When did I get outside?”. I stare down and enjoy the feeling of grass between my toes, it didn’t really matter that my shoes were still on. The wind felt soft against my body and the grass tickled my toes and legs. Still, I felt tense and uncomfortable, something was wrong. Was it just me?

As I started to get up on my feet, the sky flicked from a clear sky blue to a dark star filled sky that you could only see in a regional town. The night was chilly and filled with fog and the grass felt wet and muddy as I pressed myself up off of the ground. I hated how my hands felt, muddy and slimy. I started wiping them on my clothes, which filled me with a shame that wasn’t familiar, so I stopped, leaving me in an uncomfortable middle-ground.

I ran towards the head-building infront of me. I observed my feet stomping up the 10 steps from the perspective of a film director where I could see a candy wrapper be crushed and dragged along the sole of my feet, propelled to the left out of frame, while my legs moved right. I felt a sudden urgency to get inside the building, I burst through the double doors with both hands and shut them behind me. I thought this entrance was a motion-sensored sliding door? I looked behind me and saw a purple wave grow brighter in the fog. There was a sudden flash and purple high pressure steam pressed against the building but didn’t seem to be getting in.

I turned around to find the hallway to be incredibly well lit, lined with high-school lockers symmetrically down the hall, their colours creating a pastel rainbow colour as the lockers shift color every 5 lockers. The room felt bright and saturated at the same time. I walked down towards my locker. My shoes creating 2 distinct sounds per step, clip-clop, clip-clop, clip-clop. The clops echoed throughout the hall while the clips muted rapidly and rather jarringly.

I walked up to my locker, it was a pastel purple with a distinct marking, a cross, etched into the metal just to the right of the vents. I put in the combination by spinning the tumble lock. 2-24-45. It didnt work. 32-21-6. No luck. 204-342-654. The lock opened unsatisfyingly, requiring a little twist and tug to fully open.

I opened the pastel purple door and got inside to find the pub was full of people hiding out. Windows covered each wall and we could all observe the purple steam pressing against the building. It was a dimly lit room and the lights were flickering. There was an awful buzzing noise coming from a defective neon light from behind the bar. Despite the grim environment, the mood of the room was neutral. People spread themselves across the pub in groups, talking amongst themselves and each helping themselves to the occasional beer, spirit or wine.

I opened the fridge and helped myself to some leftovers, I took the pizza and dumplings and ate them. Both were nice, warm and made me feel hopeful.

The warmth of the food made me begin to sweat, the glaring hot sun beating down on the sandy white beach. I felt a cold loneliness and the sweat dripping down my back. The expanse was gorgeous, akin to a watercolour painting scene that contained no actors which gave it a feeling of rarity or impossibility.

This was written over a couple of days. The only goal was just to keep writing for the sake of writing while my partner was doing Nanowrimo

5 years ago

You Can’t Just: Query if a controller is for Xbox or PlayStation

I’m currently on a bit of holiday leave from my full-time web dev gig and I wanted to spend some time improving the gamepad support in my newly released puzzle game, Give Up The Dupe.

I wanted to detect if a user is using a fairly modern Xbox or PlayStation controller and then make the in-game instructions be more precise for those controllers. It’s a much smoother experience for the end-user if a game says “Press A to Jump” rather than saying “Press Button0 to Jump” or “Press [JUMP ACTION] to Jump”.

After some initial investigation, the first thing I thought to do was call one of these functions to get the gamepad name:

  • Game Maker: gamepad_get_description(0)
  • Ebiten: ebiten.GamepadName(0)
  • GLFW: glfwGetJoystickName(0)
  • SDL2: SDL_GameControllerNameForIndex(0)
  • Browsers: navigator.getGamepads()[0].id

You might think that we would be able to at least fuzzily determine what controller we are using with these functions but unfortunately it’s not that simple. As you can see with the examples below, you’re at the mercy of your driver, engine or browser when it comes to getting a name for your gamepad.

For example, my unofficial Xbox One Controller gave me these string values:

  • Windows/GLFWv3.2: “Xbox 360 Controller”
  • Windows/GLFWv3.3: “Xbox Controller”
  • Windows/Game Maker: “XInput STANDARD GAMEPAD”
  • Windows/Chrome: “Xbox 360 Controller (XInput STANDARD GAMEPAD)”
  • Windows/Firefox: “xinput”

My offical PlayStation 4 controller gave me these values:

  • Windows/GLFW: “Wireless Controller”
  • Windows/Game Maker: “Sony DualShock 4”
  • Windows/Chrome: “Wireless Controller (STANDARD GAMEPAD Vendor: 054c Product: 09cc)”
  • Windows/Firefox: “054c-09cc-Wireless Controller”

Detecting the controller type via this string is probably not going to end up not being very reliable for PlayStation controller cases, especially in browsers.

Oh well. At least the controller layout of a modern Xbox and PlayStation controller is identical, so I should be able to map some reasonably good default controls. ie. “A” to Jump on a Xbox controller, “X” to Jump on a PlayStation controller.

If you use Game Maker or SDL2, you get a nice abstraction that allows you to achieve this easily, but unfortunately for us, we’re using a low-level gamepad API. This means that each gamepad could have up to 32 buttons and the “A” button on an Xbox controller could be mapped between 0 and 31, the same goes for a PlayStation controllers “X” button. There is no guarantee these two controllers will use the same button slot.

In reality, you’ll find that most or all Xbox controllers will map the “A” button to “Button 0” on a gamepad and all other buttons generally map to the same slots across various Xbox controllers. Unfortunately, a PlayStation controller will map the “X” button to the following button slots:

  • PlayStation 4 controller: “Button 1” on Windows, “Button 0” for iOS.
  • PlayStation 3 controller: “Button 0” or “Button 1” on Windows
  • PlayStation 1 or 2 controller: “Button 2” on Windows

This means that we can make a good default user-experience for players that use an Xbox controller but we cannot make any assumptions about PlayStation controllers that will “just work”. It’ll take some more dev. effort to provide first-class support for those.

So, assuming we are willing to do the work, what can we do? Well, thankfully there is an open-source controller mapping database that we can leverage! In-fact its the same data utilized by SDL2’s higher-level gamepad API. This means we don’t need to buy a bunch of different controllers and test them, other people have donated their time and effort to give us more mappings than we could have ever reasonably achieved by ourselves. Yay community!

So we have a large set of controller mappings thats regularly updated. How can we use this? Well, on most platforms there is a function that gives us the controllers GUID, which looks something like this for my respective controllers.

  • Xbox One Controller: 78696e70757401000000000000000000
  • PlayStation 4 Controller: 030000004c050000cc09000000000000

The above values were taken from glfw 3.3 for Go. I also tested calling Game Maker Studio 2’s equivalent function gamepad_get_guid(id). My Xbox One Controller returned an empty string and the PlayStation 4 Controller returned the same GUID as above.

Anyway moving on. We can utilize these GUID values by searching through the aforementioned controller mapping database. In my searches, I discovered that:

  • The Xbox One Controller does not have a mapping that matches my GUID (and with engines like Game Maker, I just get an empty string!)
  • The PlayStation 4 Controller does have a mapping that matches my GUID

With these facts in mind, if we really want to support PlayStation controller mapping by default, we can load this open-source file of game controller data, use that to determine button defaults and infer the controllers shape, which would allow us to display more accurate controls on screen for PlayStation controller users.

So… at a high-level, To get both Xbox and PlayStation controllers supported, I think we want a solution that does something like:

  • Lookup GUID in this community-sourced gamepad mapping list. You probably want to make this file embeddable into your code so it easy to update later down the line. Infact, its worth noting that SDL2 already embeds this exact code into its high-level gamepad drivers.
  • Check the name associated with the GUID in the text file to determine if its a Xbox or PlayStation controller.
  • Fallback to using a function like glfwGetJoystickName or gamepad_get_description and checking if the name contains words like “Xbox”. We will need this logic if we want Xbox controllers to be detected as we may not even get a GUID for certain controllers or on certain platforms.
  • If we are unable to detect the controllers shape, then fallback to standard gamepad mode and just display buttons as “[JUMP ACTION]” or “Button0”.

Please keep in mind I’ve not implemented and shipped something like this yet, so I’m not sure if there are problems with this method that I’m not foreseeing. In anycase, attempting to do this small quality of life change ended up being a much bigger rabbithole than I expected, so I thought it was important to share what I discovered during this exploration.

6 years ago

Learning Cypress.io

While researching alternatives to Jest, I stumbled upon the testing tool Cypress.io[1]. It promised to make testing pleasant at the cost of only supporting Chrome. It seemed like an even trade, so I decided to give it a go.

In my very limited experience working with other testing tools, such as Selenium and Codeception, I’ve found that writing tests is incredibly painful because you would ran into timing issues that would be hard to reproduce on other machines, such as waiting for DOM elements to appear within the timeout limit. Apparently, Cypress doesn’t get these kind of issues because it executes directly in the browser.

So feeling inspired and hopeful about the future of web development, I decided that I would try and build a relatively simple vanilla JavaScript component like an accordion. I’d also want to try and unit test it with Cypress, even though there’s no first class support yet.

But first, let’s setup Webpack

It was Friday and I know that the biggest demotivator to doing frontend work in my spare-time is getting Webpack setup. With this in mind, I decided that I’d try and get a basic config setup in about an hour, just so that I could hit the ground running with coding and hacking at the problem the following day.

Needless to say, I underestimated Webpack and I’ll need to continue just getting my tools setup tomorrow. Dang.

Fresh Start

I got up nice and early, feeling motivated to fix my Webpack setup and excited to experiment with Cypress. The first issue I was greeted with was a nice error message from Cypress, telling me that it had trouble parsing my code and that my Webpack configs may be incorrect. Thanks Cypress!

Cypress Error Message

After resolving my transpiling issues by following the some of the Cypress documentation and looking at the Github examples, I then tried to run Cypress again.

It crashed.

I got an error with Cypress attempting to require “babel-loader”. My webpack config contained nothing to do with “babel-loader” and it felt… incorrect to me that it was a requirement.

Rather than install babel-loader and keep moving forward, I wanted to understand why this was happening. So. I dug into the package where the issue was occuring and discovered that the default Webpack config that Cypress tries to utilize will call “require(‘babel-loader’)”, regardless of whether you have your own config or not.

I raised a Github issue describing how I commented out the default config and how Cypress was working without babel-loader. To resolve this problem, I proposed that babel-loader is only require()’d if there is no override. Shortly after, I raised a pull request to fix the problem. Most impressively, someone from the Cypress team responded within 3 days, accepted the PR and had it tagged in NPM as a patch version, awesome. Just awesome.

Babbys First Test

Once I had resolved the babel-loader issues, I got a very basic TypeScript test working. It checks to make sure that the number 42 is actually the number 42! (Hint: It checks out)

describe('TypeScript', () => {
  it('works', () => {
    // note TypeScript definition
    let x: number = 42
    if (x) {
      expect(x).to.equal(42)
    }
  })
})

Smile

Cypress has been incredibly pleasant to use so far, it treats me like a human being. Nice readable error messages and built-in guides, telling me what I should do next. It even told me within the application to start a seperate local web server and run “cy.visit()”, nice one! I love a built-in tutorial!

Whilst reading up on how this would all work and tie together, it felt a bit too heavy to me. I don’t want to have to think about spawning two processes on something like TravisCI, I want things to be as simple as possible. So, because this is my spare time, I figured why not ignore the warnings on their documentation page completely and just try and experiment? What could go wrong? How much time could I really waste going down this path?

Have a break

I decided to stop there for the time being and play a bit of Overwatch with my partner. Between matches I tweaked Webpack config files and installed SASS so I could also test styling.

Node SASS no likely

I tried running my code only within Cypress.io and… no_good_emoji. node-sass started getting errors that seemed to be related to my installed Node version, it seemed to be expecting Node 8.10.0 instead of 10.6.0. I thought that perhaps NVM for Windows was being a bit janky with Cypress, so I decided to change my Node version from 10.6.0 to 8.10.0 . After hitting more errors related to the node-sass DLL being incompatible, I deleted my node_modules folder and re-ran yarn.

Argh. More errors!

Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 8.x
./src/Collapo.scss (./node_modules/css-loader??ref--4-1!./node_modules/postcss-loader/src??postcss!./node_modules/sass-loader/lib/loader.js??ref--4-3!./src/Collapo.scss)
Module build failed (from ./node_modules/sass-loader/lib/loader.js):
Error: A dynamic link library (DLL) initialization routine failed.
\\?\D:\wamp64\www\collapo\node_modules\node-sass\vendor\win32-x64-57\binding.node
 @ ./src/Collapo.scss 2:14-192
 @ ./cypress/integration/spec.ts

Troubleshooting

I started trying to fix the errors by loosely following a Github issue, I tried running the command:

yarn add --force node-sass

But that wasn’t working. I next tried running Cypress directly, instead of through Yarn. By that I mean I executed:

node node_modules/cypress/bin/cypress open

instead of:

yarn cypress:open

I tried this because I thought that Yarn could potentially be causing an issue with how environment variables were being passed in. Unfortunately this didn’t work either and so I tried to re-install node-sass at the latest version with the following command:

yarn remove node-sass
yarn add --dev node-sass@latest

But I still can’t get it working! I’m honestly tempted to just circumvent this issue by trying an alternative CSS preprocessor, like Stylus, as all the issues seem to be related to node-sass loading a DLL. However… since SASS is generally the agreed upon preprocessor and that my last 2 places of work were heavily invested in it, I’d rather my code be compatible with a toolset I use day-to-day.

I gotta tell ya, I’m feeling pretty frustrated by now, and really, these problems wouldn’t have happened if I just did what the Cypress documentation said to do, and ran the code on a seperete local webserver.

Time for a break.

Every day, once a day, give yourself a present

I decided to go for a walk down the street and got a hot chocolate with my partner. It’s a nice sunny day and stopping to have a chat and a treat is a good morale boost!

Back to business

I decided to have another crack at trying to get SASS working. So I decided to take a look at the sass-loader Github page to see if there was anything I could do to just straight-up avoid this problem rather than solve it. My intuition and past research[1] was telling me that node-sass might lead to further issues when running on a CI server, so if I can drop it, it’ll probably save me future headaches as well.

After reading about the sass-loader on the Github page, I noticed that the node-sass implementation is being dropped in favour of dart-sass (“sass” package), after reading about it, I decided to give it a go.

Oh yeahhh! Now when I run Cypress, I get no node-sass related errors and I don’t need to run a seperate local webserver, at least not yet! My stubbornness has paid off! I’m pleased that I found a drop-in replacement for node-sass that I can perhaps apply in my day-to-day!

module.exports = {
  module: {
    rules: [
      {
        test: /\.scss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          {
            loader: require.resolve('css-loader'),
            options: {
              // todo: Jake: 2018-11-11
              // Change this loader to "2" if we use postcss below
              // https://github.com/webpack-contrib/css-loader#options
              importLoaders: 1,
              sourceMap: true,
              modules: true,
              localIdentName: '[name]__[local]',
            },
          },
          // todo: Jake: 2018-11-11
          // Install this so -webkit-* prefixes get generated for IE/old iOS
          /*{
            loader: require.resolve('postcss-loader'),
            options: {
            // Necessary for external CSS imports to work
            // https://github.com/facebookincubator/create-react-app/issues/2677
            ident: 'postcss',
            },
          },*/
          { 
           loader: require.resolve('sass-loader'),
            options: {
              implementation: require('sass'),
              includePaths: [
                path.resolve(__dirname, 'src')
              ]
            }
          }
        ],
      }
    ]
  }
};

CSS Modules and me

declare module '*.scss' {
  const content: {[className: string]: string};
  export = content;
}

I wanted to import SCSS files in my TypeScript files, so I added the declaration.d.ts file you see above to my project.

But… it it doesn’t seem to be working. When I try and import a stylesheet with my current Webpack config, the imported object is an empty JS object. Ergh. At this point, I’m heavily considering biting the bullet and running this in a seperate server. But before I do so, I want to attempt to build the output JavaScript and CSS files without Cypress, just to determine whether this is a Cypress issue, or something else entirely.

After googling a little, I discovered a React Github issue where someone had the same issue as me! Unfortunately, it didn’t relate to how I had my Webpack config setup. I’m not using React. Bummer! It did instead make me consider that maybe this issue was due to my Webpack configuration.

I looked at my loaders and did a bit of googling on each of them. Eventually, I discovered that the MiniCssExtractPlugin is not meant to be used for development builds and I tried commenting out the plugin… and…

Success! I can see the classnames in the stylesheet object I’m importing! But… we’re not quite there yet. The stylesheet I’m importing isn’t applying to the window. The button still looks ugly!

Dive into the DOM

I inspected the DOM with the Google Chrome DevTools and dug around. I discovered that the Cypress test script outputs in it’s own sandboxed iframe and that the stylesheet was outputting inside this iframe and not in the iframe that contained the rendered button. After discovering this, I decided to try and hoist the stylesheets out of the test iframe and into the buttons iframe. It worked! Now everything is running in Cypress! Also, by the way… iframe. iframe. iframe.

The final stretch

Hooray! Things work in Cypress! All is well and good right? Nope!

My production build no longer outputs a file. Up until this point, I was trying to do everything with a single Webpack config file but it was at this point I decided to just split it into 3 files. common, dev and prod.

I struggled for a little while trying to get my configs to be as concise as I wanted because I didn’t read the documentation to see how merging worked. So, I decided to look at the webpack-merge documentation. Lo’ and behold! It can do smart merging for me, which allows me to prepend loaders in my dev and prod configs, this helped reduce config duplication across dev and prod.

Phew! With all this done, I’m pretty happy! I think I’ll leave it here for the day!

If you want to see where I left this project at, you can see it on Github here. I’ve also attached my raw notes that I took as I went through the dev process, I used these to recall my process and feelings so I could write this blog post.

7 years ago

Golang Yarns: Unused variables is an error

Hey there friend! In this post, I’m going to tell you about my experiences with unused variables giving a compile error in Golang, from my first impressions to lessons learnt after months of using the language on a hobby project.

Frustration

When I first hit a “declared and not used” error message, my initial reaction was “What flag can I use to turn this dumb ‘feature’ off?”

After hitting this error message a few times and feeling the frustration of this “unnecessary error”, I jumped onto Google to try and understand the reasoning behind this design choice. I discovered at least one reason for this was to stop unnecessary importing of packages, which could lead to unnecessary bloat in your binary size.

When I read this, I felt partially satisifed but… I still felt annoyed by it. It just didn’t seem that helpful, especially when you consider that I was simply doing hobby work, I didn’t need this particular benefit.

Satisfication

After months of programming in Go for about 20-30 minutes a day on my commute to work, I found with experience that this feature also offers at least two more subtle benefits.

The first is that if you shadow a variable but forget to use it in the shadowed scope, you will get an unused variable error. If you’re like me and you have trouble visualizing pseudo-code from plain English text, here’s a code sample for clarity:

package main

func main() {
    myVariable := "value"
    if myVariable != "" {
        // compile error! "myVariable" isn't used!
        myVariable := "shadow"
    }
    myVariable = "not value!"
}

This hidden benefit probably saved me 10-20 minutes of debugging and it came from two seemingly unrelated systems complementing each other.

The second benefit you get is that your code becomes easier to read, both to yourself, others and also you in ~3 months. If your code was recently compiled sucessfully, you now have the guarantee that there are no unused variables in your program. I really love this reduction in noise and I get a warm fuzzy feeling knowing that the compiler isn’t doing redundant work.

The Disciplined vs The Nanny State

So I’ve talked about all the benefits of this feature, but what are the tradeoffs? What are we losing with this feature?

Well… we are potentially losing effeciency if our workflow is to make a big mess and then cleanup our code before committing. I think any kind of writer can attest to the idea that editing as you go isn’t the most effiecient way to pump out a book. It’s a lot easier to tidy things up and improve concision when you’re working with a more complete piece.

A veteran game programmer will find this feature infuriating. After all, this could simply be a warning or be ignored for “dev” builds.

In theory this is a good idea and incredibly easy to implement. However in my experience working in web development, developers tend to do the bare minimum, will ignore warnings and if the codebase got to a point where fixing errors that only happened in “production” mode was too much effort, the developer would most likely just ship “dev” builds because it’s easier on them.

Conclusion

University says, Don’t call your conclusion “Conclusion”, but I say, nah, she’ll be right.

So what’s the conclusion here? I guess… in conclusion it’s that we should keep in mind that if something seems like a bad idea on the surface but a group of people with decades of programming experience says its a good idea, then perhaps the value won’t become apparent until you also gain some experience.

Related resources

7 years ago