My online multiplayer mod for Caveblazers was released last week!
You can download it via Steam for free if you own the game.
This is a post about how that went.
: Two weeks ago by time of publishing because I'm bad at scoping blog posts
As of late, the thought process behind my modding works can be outlined with the following:
Between having quick doodles and a largely-text post, I choose doodles
And often goes like this:
Me: Hello, would you like some online multiplayer in your game?
Me: Also, here's a mockup mod of how this should work
Remarkably, as I seem to slowly approach status best described as "that person that's done some netcode for pretty much every high-profile GameMaker game", the odds of people accepting had been growing, but so have been the odds of people just timely hiring me to do online multiplayer before their game is done and I could start wondering if they'd like online multiplayer or not.
So, in this case, I messaged Will (developer of the game) mid-November 2017, pitching the idea of the online multiplayer mod and the general terms for it (releasing as a mod on Steam store and all), along with an (admittedly very hacky) prototype version.
After a few delays everything was confirmed, Will contacted the game's publisher, the publisher contacted their lawyer to write a formal contract, and, fast forward to March 2018 (everyone were busy), I finally could start making the actual thing... well, except at the time I was still doing some code for YoYo Games, so I wasn't able to properly work on it before April.
The development went as following: I would test the game with whomever that had time to do so (most often, makzor) for a few hours (or until a game-breaking error), take notes, and fix things. Then test again a few days later.
Finally, by late July the mod was seemingly ready for a public beta.
Comedically, as both beta and my long-overdue vacation seen slight delays, the launch of beta coincided at the exact day of me leaving for the vacation, prompting me to take the laptop with me.
Honestly, bringing a laptop on a vacation is a pretty bad idea, as then it is tempting to do some work while there, but also wasn't such a bad idea, because, aside of some pleasantly round mountains and various trees climbing their slopes, there wasn't that much to see.
The next months came and went as far as mod development goes - time to time I would squeeze in a few days time between other projects, fix the issues, and roll out a new beta build. Then people would find more bugs, I'd later fix those, and roll out another one.
By early January 2019, it occurred to me that there don't seem to be any more game-breaking bugs left. This means that it's time to release the mod, right?
It didn't occur to me that I'd need a trailer for a mod. For games you obviously want one because it's the first thing the potential buyer sees, but for a freeware online multiplayer mod... you kind of know what you're in for, don't you.
So, I started asking around to see who'd be available to make me a tiny trailer, and, lo and behold - everyone were busy. As few weeks passed, I ended up asking Will if he knew anyone that's available, and the small trailer was finally done by a Yogscast video editor, Alex.
Fast-forward through a few more weeks of obligatory Steam pre-moderation, trailer/depot checks, and a two-week pre-release interval, and the mod finally released on March 7! And everyone seemed pleased. And nothing major broke, which seems almost suspicious.
To be fair, I've already done this ten times over, so I knew exactly what to watch out for. There have been some hiccups due to dealing with GameMaker Studio 2's spatial collision system for the first time (more on this later), but overall things went pretty well.
This being a mod and all would, of course, mean that there were some scope implications due to budget constraints (or, well, lack thereof), but also that requirements were a little softer - for instance, I probably wouldn't get away with shipping a title with a netplay menu that is completely functional but looks like this:
But, it's not all budget cuts - you also get a really fancy chat with plenty of commands, settings, and all features that you would expect from one:
And, for a change as far as modding goes, being able to ask the developer on how something works was welcome - usually you kind of just poke at things until they work.
Much like Nuclear Throne, Caveblazers had the beginnings of a local 4-player multiplayer support, and the idea was similarly canned for the reason of there simply not being enough screen space to fit 4 players.
But, now that you can play the game online, screen space is no longer the concern - you can have as many players as you want!
Or, well, as many as the game's balance can hold, which is about 4 really - while the players will have to split resources between them, their potential damage output grows linearly, and many of the game's foes and bosses don't really have the capacity to fight multiple players at once.
Still, in spite of lowered (or increased, should your friends keep dying all the time) difficulty, playing with 3 or 4 players online is lots of fun!
Or, should you have an ultra-wide monitor or a 4K TV, you can play >2P CBT locally too:
While working on online multiplayer, I found that having chat commands means that you can add arbitrary quantities of uncommon options without overflowing the settings menu, so I did:
- Mouse aiming
While Caveblazers is designed for gamepad, it is understandable that some players are more familiar with aiming with mouse than keyboard/gamepad, so you can have that - both for shooting and for inventory management. The option resides in control settings.
- 360-degree aiming
Similarly to above, 8-directional aiming is part of the game's original design, and boss patterns make active use of it (as you could otherwise just shoot them from a room corner like a coward), but, if you really want 360-degree aiming - sure, you can have that via /aim360 command toggle.
What's the catch, you ask? Orcs and other enemies will also gain 360-degree aiming
Finally, you can fulfil your dream of being shot by all orcs on the level at once
- Directional aiming
Somewhat opposite of above: if you are really used to metroidvania controls, or would like to play Caveblazers on a controller with no analog sticks, /moveaim is a solution to that - when pressing the "shoot" button, the game will take not of your currently held movement keys, and you will continue to shoot in that direction until you release the button.
- Inventory auto-sorting
An awful lot of rogue-like/lite games at some point start involving increasing amounts of inventory management, Caveblazers included. /autosort can help with that, automatically grouping items by category.
- Quick start
If you are practising for the game's more challenging modes, you might find it slightly frustrating to play the first few stages over and over in search for a viable gear combination. This option aimed at making that a little easier, skipping the first level and dropping you off in a randomized shop with the amount of money that you would usually get on the first level. Activated via /startmode quick, deactivated by /startmode normal.
- Coop revival modes
By default, if a player dies in Caveblazers, they will enjoy the rest of the level as a ghost, and will be revived at the beginning of the next level while losing half of their unequipped items and half of the blessings (power-ups), along with any stat bonuses that they earned.
This is okay for more experienced players (after all, in single-player modes you by default don't revive at all), but, if you are trying to introduce your friend(s) to the game, for the first few hours they might spend most of their time as ghosts while constantly losing everything.
/revmode arena changes this to match behaviour used in Arena Mode - the players will only lose stat bonuses, while keeping their items/blessings.
- Debug commands
Finally, the sizeable set of debug commands in CBT lets you easily practice bosses, try out items without re-rolling, and generally get a good look at how everything works.
Here's a strange thing about optimal conditions for making large-scale mods for games: You want the game to be somewhat stale in terms of content updates, but to still have an active community,
- If there's no community, chances are that you are doing your work for no one.
- If the game is being actively updated, every update will cost you time to update the mod for it.
Should your mod affect enough of the game, instead of porting your mod to the new version of the game, you might have to back-port the changes from the new version of the game to whatever form or compiled code presentation that you were using for your work.
Should a large game update also release, that's basically the end of the road for your mod
- you'll have to manually port your work over to the new version, figuring out what changed.
(this is why large-scale game mods often die off whenever a major game update releases)
Not to experience such specific kinds of joy, I proposed to have the mod as a "fork" of the main game - this way smaller changes could be merged to the mod automatically, and any tweaks/fixes that I make for the mod could be brought back to the base game, if needed.
So we did that, and everything was fine for a while.
Then, around May 2018, it was decided to finally move the game to GameMaker Studio 2.
That was done, and all seemed fine, until, a few weeks later, I learnt from Will that he had independently imported another copy of the project to GMS2 in preparation to console support, and had been working on that for a while, intending to merge to the repository with my code later.
While GMS1 relied purely on resource paths to differentiate resources (thus this plan would have worked there), GMS2 uses UUIDs (a long, random-ish string, designed to be unlikely to match another of it's kind), and will generate a new set if re-importing the same GMS1 project.
In other words, we now had two projects that were impossible to merge automatically. And, as both of us had made thousands of lines (of code) worth of changes, not very viable to merge manually either - the very problem that I was trying to avoid!
While I later wrote a tool to match up UUIDs between projects, it was still very much unmergeable, so matching the mod to a new base game update would usually mean that Will would copy over the non-console changes from commits in the other repository, I'd make a new build, and then it'd take a couple more builds to iron out minor inconsistencies.
Still, this'd cost more than enough in hours and unwanted headache over the development, and means that online multiplayer couldn't be "just" merged back to the base game later for console support/etc.
If you compare CB and CBT's stats, the game generally fluctuates between 30-70 active players, while the mod goes between 5-15, which seems about right - by a vague estimate, anywhere between 1/10 and 1/6 of all players might be interested in online multiplayer.
But, if you were to ask Steam Spy, you would find that the game has "anywhere between 100 000 and 200 000 owners total" while about 3500 people own the mod.
In other words, while the mod might be of interest for a sizeable portion of game's players, most likely simply haven't heard of it, and this will likely remain to be the mod's largest problem - even while being kindly featured on the game's store page.
And it is hard to blame anyone - with so many games coming out these days, it is barely feasible to keep up with new titles, let alone check out updates for the games that you've played a while ago.
If I were to go back and change something, I would probably have tried out releasing the mod as "early access" right away, even if it'd mean getting complaints about stability for a bit.
Not to end this on a sad note, I present you with a small collection of fascinating issues that I have encountered during development
If you have played Caveblazers before, you might have seen that there are plenty of small visual effects - sparkles, damage numbers, even UI effects when your magic item recharges.
And this is where the aforementioned spatial collision system comes in.
The idea of it is that the system will automatically divide your level into smaller portions - this way, instead of checking whether your projectile might hit every single enemy on the map, it only needs to check the closest 5 (or whatever amount that's in it's nearby region).
So, if a visual effect appears in a different spot for you (camera no longer shared and all that), collision system could split the level ever so slightly differently between you and other player(s).
Then something else will cause a different region to be split a little differently, and, fast forward to a few minutes later, you now hit a different orc from an overlapping group. Everything goes downhill from there. The butterfly effect as it is.
This took a little bit to figure out because I've not set up tracking for every single object type from get-go, and this would not yield any other symptoms until it was too late (with the offending visual effect most likely gone by then).
Later on, another project ran into this same kind of issue, and a change was made to GMS2 to only count instances with any actual collision checking for subdivision.
Lava in Caveblazers is very fancy - it glows, it makes splashes when objects are thrown into it, it'll even make tiny "burps" (loose particles flying out of it) while idle! Attention to detail as it is.
Speaking of tiny burps, however: as all of these things obviously have a certain processing cost, lava will slightly limit its activities when it is not being observed.
And, as there was simply no reason to worry about such things, the lava would use the same pseudo-random number generator as the enemies.
Combine the two, and there you have it: if you were to shake a lava lake that only you can see just hard enough), and thus the lava lava had no reason to "burp", your game would run out of sync with other players as enemies, projectiles, and all other kinds of entities would now yield different values due to a few "missing" uses of PRNG (meaning that later numbers would be different).
Your ultimate nemesis
This was easily the longest-standing bug in the history of the game, if not one of the bugs to escape the longest among latest projects that I worked on in general.
If you've played CBT during the beta, you might have encountered it once or twice - everything goes completely as usual, then someone's character dies on the floor prior to a boss floor, and, upon entering the boss floor, you would sometimes discover that their character is just gone - no player, no ghost, not even a UI panel in your corner of the screen:
This is what you'd see for the rest of the game unless doing tricks with chat commands
The mod inherited this issue from the base game, and Will couldn't help - he'd seen it maybe once during development of coop mode, and most CBT players would only encounter it once per 20-30 hours of playtime (perhaps even less often).
The bug resisted any attempts to debug it - at one point I would even specifically play the mod with someone while having the debugging tools open (which, in general, meant playing at an absolutely garbage framerate), and we couldn't get it to happen once.
A glimmer of light was finally shed on the issue when the built-in function for returning call stack in GameMaker was expanded in one of the updates to include information about what code resulted in destruction of a game object.
So, I made the mod log this kind of information at pretty much every opportunity, pushed out a new build, asked people to test it, and, a few days later, we finally had a sample
The kind of information logged - not pretty, but enough to debug
And, what would you think - the issue was that your ghost could get crushed by a door!
See, boss room doors (pictured earlier) are sturdy, and will crush absolutely any entity that happens to be on their path as they lock down. Even a ghost.
Usually you wouldn't really notice this, as the doors only briefly close on level start (while characters didn't even pass through the entrance yet) and after everyone got into the battle room itself (meaning that the only thing you could get crushed is items you dropped), but, if your ghost perfectly lined up with the door from its position on the last level (that's about a 1/900 chance), it'd get immediately crushed as the door prepares for its intro sequence.
So I did forbid doors to crush ghosts, Will did the same for the base game, and no one had this issue ever again.
This is an issue that still exists in the current release - whomever that created the lobby (in other words, is P1) gets to click around the main menu, and, if they click Exit, the game will quit... for both you and remote players.
The reason for this is simple enough - all menu buttons in the game are wired up the same way (and have to function in multiplayer), so CBT doesn't really differentiate between them.
Not as easy to fix as one might think, however - the game also relies on P1 being present for using any important menus, as usually they are warranted to be present.
While playing online, you might sometimes want to say "over here!", but the other player(s) wouldn't know where that is if you're out of their view.
So, CBT allows you to press a button (by default, F) to place a temporary marker at your mouse position and move it with the cursor. The markers are numbered and will display at the edge of the screen even if they are off-screen, making them a perfect way to point at just about any object of interest.
Once the button is released, the marker will wait 7 more seconds before fading out.
And, as it turns out, if you spam markers really fast, eventually the game will start lagging from drawing all that text, which in turn gives you more time to spam markers, and... well, you can see where this is going.
While it is not possible to crash the game this way (markers cost basically zero RAM), people did manage to get down to 5FPS from just having multiple players spam thousands of markers.
What secrets does the future hold? Are any of them shaped like burgers?
You might be wondering as to what's next with the mod.
The primary task would remain to keep the mod updated as new game versions release.
In sprite of aforementioned troubles I hope that this will not be overly costly.
A few people had been asking about whether I intend to add achievements to CBT.
On the technical side, achievements are certainly not hard to do, but I would like to stray clear of just mimicking the base game's achievements or making generic "do X action Y times" ones.
So we'll see if I can gather enough ideas for these.
Those that played my Nuclear Throne mod had been asking if I'm going to introduce a modding API like I did with NTT. Unfortunately, there is no easy answer to that - while such a thing can aid with keeping a game's community active during periods without updates (see: hundreds of NTT mods on itch.io) , it is also very costly (in terms of hours of work required) to do, so it'd be unwise to promise anything while knowing that I'll remain involved with Rivals of Aether development for months to come.
All things considered, I hope that you'll enjoy the work that I've put into the mod.