Holiday Hack 2024: Snowball Showdown
Introduction
On Alabaster’s side of the Front Yard Dusty Giftwrap is standing next to the Snowball Showdown poster on a small wall (that is also a terminal):
Dusty enlists me to help win the snowball fight that will take down Wombley and end this fight:
Dusty Giftwrap
Hi there! I’m Dusty Giftwrap, back from the battlefield! I’m mostly here for the snowball fights!
But I also don’t want Santa angry at us, you wouldn’t like him when he’s angry. His face becomes as red as his hat! So I guess I’m rooting for Alabaster.
Alabaster Snowball seems to be having quite a pickle with Wombley Cube. We need your wizardry.
Take down Wombley the usual way with a friend, or try a different strategy by tweaking client-side values for an extra edge.
Alternatively, we’ve got a secret weapon - a giant snow bomb - but we can’t remember where we put it or how to launch it.
Adjust the right elements and victory for Alabaster can be secured with more subtlety. Intriguing, right?
Raring to go? Terrific! Here’s a real brain tickler. Navigator of chaos or maestro of subtlety, which will you be? Either way, remember our objective: bring victory to Alabaster.
Confidence! Wit! We’ve got what it takes. Team up with a friend or find a way to go solo - no matter how, let’s end this conflict and take down Wombley!
Snowball Showdown
Challenge
On loading the terminal, it offers a menu similar to 2023’s Snowball Fight:
The mode button doesn’t seem to have other options at this time. I’ll focus on “Random Match Making”, as that’s where I got the solves. On going it, there’s an instructions screen:
I’m supposed to move and throw snowballs using my mouse to aim and shoot. When I start, the arc shows where my snowball will fire. There are elves on both sides up on platforms making the shots trickier. If anyone gets hit by a snowball, they are incapacitated for a short time:
Hits on Alabaster and Wombley are logged in the scoreboards at the top, and there timer in the middle counts down. At the end of the timer, if Wombley has more hits than Alabaster, I win.
Bronze Solution
I can play the game and actually win, but without some cheating, it only gives the bronze solve:
Silver
JS Overview
On loading the game, there are a few sources visible in the dev tools Sources tab (Chrome):
phaser.min.js
is the Phaser game engine. That’s not likely the focus here, other than to know it is a Phaser application (much like Elf Minder). elfids.js
has a bunch of UUIDs and a list of words the elves say when they are hit or are about to throw.
reconnecting-websocket.min.js
is useful for the gold solution. I’ll focus on phaser-snowball-game.js
. There is also JavaScript in game.html
. This code is largely responsible for starting the Phaser game:
document.addEventListener("DOMContentLoaded", (event) => {
const config = {
type: Phaser.AUTO,
banner: false,
transparent: false,
backgroundColor: 0xffffff,
width: GAME_WIDTH,
height: GAME_HEIGHT,
parent: 'game-container',
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
},
pixelArt: false,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 300, x: 0 },
debug: false
}
},
scene: [SnowBallGame]
};
game = new Phaser.Game(config);
});
phaser-snowball-game.js
This entire file (2114 lines) is the definition of the SnowBallGame
class:
At line 672, there’s a create
function, which isn’t that interesting except for that it sets the variable mainScene
to this
:
This is important because it gives me access to the class from the dev tools console, so anything that is part of this
in this file is directly accessible:
It’s important to think about how the game works. Each player loads the same screen in their browser. As they move around and throw snowballs, websocket messages are sent to the server:
The server manages to track where snowballs are, and what they hit. So for example, it might be tempting to mess with something like wombleyElvesThrowDelayMin
:
It won’t actually make a difference, as that value on the server where NPC throws are made isn’t changed. In fact, that doesn’t show up again in this code anywhere outside of a comment.
Another example is when the sbh
(snowball hit) websocket message arrives:
case 'sbh':
let projectile = this.projectiles.getChildren().find(p => p.id === data.id);
if (projectile) {
// already tracked on server, so we just need to animate it
this.animateSnowballHit({ x: data.x, y: data.y }, projectile, false, true)
}
break;
All that the browser handles is the animation, not the score or incapacitation.
So I need to focus my hacking on things my browser is the source of, which is how my player moves and the snowballs thrown.
One variable set at the top of create
is snowBallBlastRadius
:
In the class constructor, there’s throwRateOfFire
:
This is the number of milliseconds delay between when I can throw snowballs. There’s also healingTerrain
:
And a particularly fun one:
This impacts if I’m allows to move up and down as well as left and right:
} else if (!this.onlyMoveHorizontally && action === 'up') {
if (isKeyDown && !this.player1.isKo) {
this.startWalkAnimationTweensIfNotStarted(this.player1)
} else {
this.stopWalkAnimationTweensIfStarted(this.player1)
}
this.player1.isMovingUp = isKeyDown;
} else if (!this.onlyMoveHorizontally && action === 'down') {
if (isKeyDown && !this.player1.isKo) {
this.startWalkAnimationTweensIfNotStarted(this.player1)
} else {
this.stopWalkAnimationTweensIfStarted(this.player1)
}
this.player1.isMovingDown = isKeyDown;
}
Mods via Dev Console
When a snowball hits something solid, it takes a chunk out of it:
When I set the snowBallBlastRadius
bigger, it clears out a lot more. For example:
With this, I can take out the center block of ice and throw directly at Wombley. Also worth noting that when I start messing with any parameters, the “CHEATING HACKER DETECTED!!!” message shows up. I believe this is what unlocks the silver solve.
There is a limit to the speed at which I can throw as well, defaults to 1 second:
That isn’t that much:
If I set that to 0:
Then I can throw a lot faster:
With these two combined, I can blow through the middle wall and constantly hit Wombley:
On winning, I get the silver solve:
I can add in vertical movement or non-healing terrain as well to make it even easier (I’ll show these in the next section’s video).
Mods Via Overrides
Something I’d never played with but was inspired to look into this year was Chrome Local Overrides. This feature of dev tools allows me to save local copies of resources used on a webpage such as CSS, JavaScript, HTML, or even background XHR responses, and to modify / mock request headers by saving the resource to the local file system.
In this video, I’ll show how I can use this to modify the JavaScript code using overrides to solve the challenge:
Challenge for Gold
Dusty is pleased with the silver, but says I should look for a secret weapon:
Dusty Giftwrap
Fantastic work! You’ve used your hacker skills to lead Alabaster’s forces to victory. That was some impressive strategy!
Christmas is on the line! For a mischievous edge-up, dive into the game’s code - a few client-side tweaks to speed, movement, or power might shift the balance… or just help us find that secret weapon we misplaced!
Gold
reconnecting-websocket.min.js
This file is two long lines of JavaScript, though the Chrome dev tools pretty print it to many more. The first line is about what the name implies - reconnecting the websocket. But the second half is different, adding a bgDebug
function to the top level window
object:
This function gets an e
and checks if the type
is “moasb_start” and if the mainScene.moasb_started
variable is 0. Then it sets mainScene.moasb_started
to 1 and creates a new animation involving a bomberContainer
. This sounds like the secret weapon!
Most of the code is creating the animation that I’ll show shortly. At the end, it sends a websocket message:
phaser-snowball-game.js
In the main game, there’s a function that handles incoming websocket messages, which the game is based on. Most of this function is a large switch
statement on the type of the message. But just before that, there’s code referencing window.bgDebug
:
Each message is being passed to the bgDebug
function before it’s handled by the main switch
.
Trigger Event
There is no moasb_start
reference in either file other than the check in bgDebug
watching for that message. That isn’t surprising, as that message would come from the server.
One idea is to just send the websocket message at the end of the animation from the dev console, which I can copy and paste directly from reconnecting-websocket.min.js
:
On sending, immediately all of Wombley’s elves take a hit, Wombley’s hits go to 999, and immediately I’m granted the gold solve:
The more interesting way to trigger this is the moasb
function tucked away on line 861 of phaser-snowball-game.js
:
It sends a websocket message of the type “moasb”. I’ll run that in the terminal, and immediately there’s the moasb_start
message in response from the server:
This triggers the animation:
The bomber comes in and drops a large snow bomb on Wombley and his team ending the match instantly. There’s a few Easter eggs in here as well:
- MOASB is a twist on a real weapon, the Mother of all Bombs or MOAB.
- Riding the bomb once it’s released is Elf the Dwarf, who debuted the 2022 KringleCon talk DevOps Faux Paws, joins my team to help me win the 2023 Snowball Fight challenge, and is the main character of the 2023 Gameboy challenges. Glory!
- “Yippie-Ki-Yay mother froster” is a reference to Die Hard.
Once the bomb is dropped, my client sends the moasb
message with the launch code, which I believe triggers the solve on the server.
Interestingly, if I am in a game with another player who starts the MOASB sequence, the moasb_start
message comes to both browsers, and then both browsers send the launch_code
and get the gold solve.
Outro
Dusty is excited:
Dusty Giftwrap
Brilliant! You unravel the puzzle and launched the ‘mother-of-all-snow-bombs’ like a true mastermind. Wombley never saw it coming!
Excellent! With Wombley’s forces defeated, they’ll have no choice but to admit defeat and abandon their wild plans to hijack Christmas.