Let me tell you a tale about an initiative to reduce Flash dependencies on what used to be a "Flash gaming" site. This is the story about a large-scale project with the best possible outcome being that users don't notice any changes, and the worst possible outcome of everything breaking horribly. This, my friends, is the story of Kongregate's Death to Flashy initiative.
In the Beginning
When we first started building Kongregate, we didn't have to worry much about whether or not users had Flash installed. It was very much ubiquitous, and since you couldn't play most of the games on the site without it we could build features based on the assumption that Flash was available. We built our chat client in Flash, we created APIs for both ActionScript 2 and 3, games implemented them, and all was good.
Writing on the Wall
Once Chrome and Firefox started blocking these invisible SWFs we had to start serving them from our top-level domain as opposed to our CDN, as SWF files from the same domain as the parent page do not get automatically blocked. This temporary workaround gave us some breathing room, but it will be removed from major browsers in the near future (it has just recently been removed from Chrome), and definitely will not work anymore once Adobe puts the final nail in the Flash coffin.
Phase 1: Migrating Chat
The original chat code used some ActionScript-specific functionality (like serializing and compressing user data into an AS3-specific binary format), so we had to do some intermediate releases to resolve those issues and make things forward-compatible so that Flash and JS users could see each other as we rolled the feature out.
We added in the ability to progressively enable the feature for players, and that turned out to be key. Whenever we discovered a new bug (of which there were plenty), we could flip a kill switch and all users would be back on the Flash version of chat until we could resolve the problem.
Since IE8 and IE9 were supported browsers that don't have WebSockets available, we decided to use a Flash WebSocket polyfill for them. This technically introduced more Flash into our stack, but was seen as a good tradeoff since it was a black box 3rd party component, and it meant that all browsers would be using the new JS code path as opposed to IE8 and IE9 using the old AS3 component.
Near the end of 2016 we had completely opened the floodgates, and if you have been chatting on Kongregate since then you have been using the new HTML5 chat system, hopefully without knowing it. One advantage of the new WebSocket-based chat is that it connects using standard HTTPS over port 443, making it much less likely to be blocked by a firewall or antivirus software.
The Kongregate API consists of two major components -- the "client," which is the part that games interact with directly, and the "Konduit," which is a SWF that is loaded in the top-level page and can act on behalf of the user. This phase of the project was logically split up into porting each of those components.
The top priority for rewriting the API was ensuring that developers would not notice a difference, and that their games would continue to work. We started with porting the "Konduit" component, as games don't interact with that part directly anyway.
Once the Konduit logic was fully ported, it was time to start on the API client. The biggest hurdle for this part of the project was switching from Adobe's LocalConnection class to PostMessage for cross-frame communication. We had been plagued with
LocalConnection issues and bugs for years (if you've ever had trouble being awarded a badge
LocalConnection is likely the culprit), so this was a very welcome change.
PostMessage the rest of the work went relatively quickly.
Once again we added in mechanisms to roll this out to users slowly so that we could revert to the previous behavior when we found issues, which definitely came in handy, but also increased QA overhead, as our test game matrix had to be checked with the new features both enabled and disabled.
Phase 3: What about Flash Games?
So now that our non-Flash games are in good shape, what about Flash? Even though Adobe has announced an official end-of-life date for Flash at the end of 2020, we feel it is important to keep supporting these games as long as it is possible to play them.
At this point in the project all Flash games were still using the ActionScript client API, which makes sense, but is a bit problematic because it requires the Konduit helper SWF to be present in order to listen for messages over the always-problematic Flash
LocalConnection class. This will become even more of a problem in several months when Chrome stops allowing invisible SWF helpers to run automatically and requires user interaction (such as a click) to activate them.
This phase of the project focused on making Flash games send API messages using
always for the embedded SWF files. For externally hosted games, this will only work if the developer happened to set
Once the API SWF loads, it checks to see if the
ExternalInterface is available. If so, it checks for the existence of the Kongregate JS API. If it is not present, it loads it asynchronously, and then notifies the Flash API that it is ready for use, at which point it completes initialization and configures itself to use
PostMessage as opposed to
LocalConnection as a transport.
Since completing the DTF project our support requests for both chat connections and missing badges have been noticeably reduced, which is a fantastic side effect.
We are actively working on squashing the remaining backward-compatibility bugs as issues arise. Our QA team has been working extremely hard testing hundreds of games to make sure they still work properly with all of these changes. To give you an idea of the kinds of things we have run into, one developer happened to be creating a top-level function named
addEventListener (essentially overwriting window.addEventListener and breaking our ability to communicate via
addEventListener on the
document object and applying that to the
Window, which of course does not work in IE. The IE fix required us to create a temporary iframe object and apply that element's
addEventListener function to the
Thanks for reading along, and stay tuned for our next adventure -- "Welcome to SS-Hell!"