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):

image-20241123113522996

Dusty enlists me to help win the snowball fight that will take down Wombley and end this fight:

Dusty Giftwrap

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:

image-20241123145029618

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:

image-20241123145203983

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:

image-20241123145257652

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:

image-20241123115007666

Silver

JS Overview

On loading the game, there are a few sources visible in the dev tools Sources tab (Chrome):

image-20241123150222494

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:

image-20241123151352665

At line 672, there’s a create function, which isn’t that interesting except for that it sets the variable mainScene to this:

image-20241123151514997

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:

image-20241123151537270

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:

image-20241127124423271

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:

image-20241127124517694

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:

image-20241123151931349

In the class constructor, there’s throwRateOfFire:

image-20241123152045527

This is the number of milliseconds delay between when I can throw snowballs. There’s also healingTerrain:

image-20241127124104101

And a particularly fun one:

image-20241127124141491

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:

image-20241123153821735

When I set the snowBallBlastRadius bigger, it clears out a lot more. For example:

image-20241123153859451

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:

image-20241123153950106

That isn’t that much:

image-20241123154046772

If I set that to 0:

image-20241123154122476

Then I can throw a lot faster:

image-20241123154111507

With these two combined, I can blow through the middle wall and constantly hit Wombley:

image-20241124053729389

On winning, I get the silver solve:

image-20241124053818718

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

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:

image-20241124061459826

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:

image-20241124061708046

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:

image-20241124061931269

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:

image-20241124062302037

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:

image-20241124062432499

The more interesting way to trigger this is the moasb function tucked away on line 861 of phaser-snowball-game.js:

image-20241124062549372

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:

image-20241124062650245

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:

Once the bomb is dropped, my client sends the moasb message with the launch code, which I believe triggers the solve on the server.

image-20241124055806092

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

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.