Mess with This

An introduction to how I'm approaching modding in Reset Hard, and what the open questions are.

As a quick note before we begin, I'm going to be using a lot of language in this post that suggests that I'm either completely sure about every decision or that everything is already working. While I feel pretty confident about what I'm talking about, none of the below is set in stone, and it might change before release.

Zoomed out view of the first level

Some philosophy

I've been open since the start of Reset Hard that I'm interested in building a moddable game, partly as a way to drum up interest in the game, but also just because the game's design lends itself really well to community expansion.

Reset Hard's approach to modding is informed by four design mantras:

  • Make tools accessible: Prefer either Open Source or internally developed tools where possible. Tools should be freely available and work cross-platform.
  • Prioritize low-level APIs: It will always be possible to build high-level abstractions on top of a flexible, low-level API. The opposite is not true. I prefer to offer low-level tools when possible, even when the API is slightly more cumbersome or complicated.
  • Be transparent: Modders should be able to examine existing levels and sections of game code to determine how Reset Hard works. Reset Hard is not an Open Source game, but I’m not going out of my way to be too coy about how it’s programmed or what its internals look like.
  • Eat your own dog food: The tools I’m providing for modding are the same tools that I use internally to build the game. The API for user levels, entities, and sprites will be the same APIs that I use when building levels, entities, and sprite.

Over the course of this post, I'll be going over some of the ways that these mantras have influenced the game's architecture.

Electron and Javascript

Reset Hard is written using web technologies and then packaged for the desktop. Neither Javascript nor Electron are commonly used for commercial games. There are downsides to me choosing these technologies, including performance, memory management, and frame timings.

However, for a game like Reset Hard the benefits outweigh the negatives. As far as modding is concerned, leveraging Electron makes it easier for me to expose internal APIs and to allow players to override interfaces and imports.

Javascript is an extremely accessible, flexible language, which should decrease the barrier of entry for new modders. It comes with good debugging tools, which makes it easier to prototype and play with new ideas. It also has some promising sandboxing features, which I'm still looking into, but may also enable players to install mods without doing a ton of extra validation that they're safe.

Client/server separation

Reset Hard's client is written in HTML, Javascript, and CSS. Where possible, the interface is laid out semantically. This should make it easy for modders to customize the basic layout via CSS. Additional scripts can also be attached by editing the HTML directly.

DOM representation of Reset Hard interface

The actual game engine of Reset Hard communicates with the front-end over a documented protocol. I will support communication over websocket even though the main client will be integrated to reduce lag.

This means that aside from modifying the client directly, modders will also be able to create entirely separate clients and visualizations. The websocket protocol will also enable modders to remotely script player input to build AIs or assistants.

The game can already be run in headless mode, so I'm considering exposing that as an option in the final build to make it easier to host remote servers and build interesting remote/scripted experiments.

Custom entities and overrides

Reset Hard uses NodeJS as its runtime, and in that spirit the code is highly modular. Modders will be able to import public interfaces both to use and extend in their own code.

I'll provide an entry point where all of my internal interfaces are loaded.

var ENTITIES = {
    player : require('entities/player')
    switch : require('entities/switch'),
};

module.exports = ENTITIES;

When I am loading the lists of entities and utilities into the main game, I will merge active mods on top of that import list.

I'm still ironing out the details about how mod bundles will work, but basically it will boil down to providing a config file that uses the same interface that I do for internal development.

var myMod = {
   entities : {
      switch : require('./my-switch-override.js'),
      customEntityType : require('./my-custom-entity-type.js')
   },

   levels : {
       intro : require('./overrided-intro-level/main.js'),
   }
};

module.exports = myMod;

Getting away from the technical details, what all of this means is that modders will be able to override internal modules and entities, and those changes will be universally applied across the entire game.

Level editing

In keeping with my first modding mantra, I use an open source level editor, Tiled, to build puzzles and levels in Reset Hard. Tiled is freely available for Mac, Linux, and Windows.

If you want to programatically build or modify a level, Tiled's export format is well documented JSON. This means that in addition to having access to the raw tools that I use, modders will be able to build their own.

Tiled is the level editor I’m using for development

One nice thing about using Tiled is that its export format is lossless. This means that any of the packaged levels for Reset Hard will also be able to be opened directly in Tiled. This will hopefully making modding more accessible, since if you see me doing something cool in a level, you'll be able to open it up and directly see my implementation.

Outside of Tiled, named entities within a level can be wrapped with custom scripts, allowing modders to specify per-level behaviors. A level in Reset Hard is really just a directory with a map file, an entry point, and scripts.

The level format in Reset Hard is still evolving, so I won't go into too much detail about how exactly everything works. But suffice to say, I am enormously proud of how flexible the current system has turned out to be.

What I'm working on

There are, of course, problems that I haven't tackled yet.

  • Resolution/framerate independent animations and sprites: I’m still trying to figure out what the best way is to allow modders to add additional animations, or to override the art-style and resolution entirely.
  • Translation support: I actually have a fair amount of experience with building software that can be easily translated, so I’m not worried about this. But there are still a few things for me to think about here.
  • Sandboxing: I am a big believer in sandboxing software to protect people’s computers. There are a few promising options here for Javascript, but I’ll need to do more research and testing before I can trust any of them. Any amount of sandboxing will be a win, but obviously more is better.

Mod support will be an ongoing task within development. My first priority with Reset Hard is getting the game done - this is why I haven't taken the time yet to worry about things like translation support or sandboxing. None of these topics make me worried, but it will likely be a little while before I have the time or resources to tackle them.

Building out a community

I've been mostly talking about tools, but tooling is only one half of building a moddable game. The other half is fostering a community.

There are a couple of really promising directions that this can go. I've been talking to some people in industry about options, and I am very optimistic about the potential of Mod.io as a way to integrate mod loading into the client itself. Obviously this wouldn't support some of the more invasive mods like full client replacements, but it would be an interesting way to help promote and distribute custom level packs.

However, there are additional questions that integration with services like Mod.io bring to the table. Mainly, 'how far should this integration go?'

There are a lot of potential answers to that question.

  • The game allows you to browse curated level packs.
  • The game allows you to browse full-fledged curated mods.
  • The game allows you to browse uncurated mods.
  • The game allows you to load and manage mods, but doesn’t give you the ability to browse them.

And those options are really just scratching the surface.

Figuring out how mod integration is going to work, figuring out if the broader community wants even wants it, and figuring out what the security and quality risks are for the main game is going to be a reasonably large job. As I get closer to making decisions about this, I'll start reaching out to more developers and community members to get their feedback.

The possibility is there to make something great with integrated mod installation and browsing. My main concern is making sure that I don't mess that up or miss out on building something really amazing.

Why care about this?

Modding is a great way to extend the life of a game, and it's a great value proposition for new players. It's a great way to boost community interest in a game, and it's a great way to leverage a community as a source for additional content. But while these are all good side effects that I want to take advantage of, they are not the entire point of what I'm doing.

The point of modding is to allow a community to gain a sense of ownership over a game. It's to give users the ability to address individual problems and concerns. It's to turn consumers into creators by giving them tools to express themselves outside of a designer's original intent.

I'm sure that there will be games in the future that I won't want to see modded; where the experience I'm sharing is either so personal or so direct that I am not interested in seeing it translated or changed. And in those cases, I'll probably ask the community to take their stuff someplace else where I don't need to see it.

But even if I never end up documenting everything, or creating curated channels, or building special tools, or even interacting with that community at all, at the very least I'm going to avoid unnecessarily burdening or restricting someone who wants to mess with my IP. And that's because, frankly, modding is not about me.

I don't believe that I should get the final say about how people consume or interact with the things that I build. So with Reset Hard, I'm taking that attitude to the extreme — opening up as much of my internal implementation as possible, and building an engine that allows players to extend and replace nearly anything.

I believe that healthy communities will nearly always take some kind of cultural ownership over the IPs they're built on. With Reset Hard, I'm just making that invitation a little more explicit.