59

Also see: Hat Dash White Hat Hall of Fame and Leaderboard Refresh

Update 2021-01-13: All bounties have been awarded (on this post or on the other one), thanks all for participating!

As some users have reported, some suspicious scores are beginning to crop up in hat dash. I have some heuristics and rules in place (more to come as well) to catch some of these automatically (or with a few manual steps from me). That said, I am interested in keeping things fun and honest for all.

So, if you are able to (in an answer to this question) repro a way to successfully cheat, in such a way that the score appears on the board (and is not already on my list of things to look out for, like the person who scored 999K in 20 seconds), or suggest a new effective heuristic beyond what is already in place for detecting cheating, then I will do the following:

  • Award your answer a bounty (value TBD based on the severity)
  • Give you a shout out at the bottom of the hat dash leaderboard

Cheating = either through direct manipulation of the API, or something client side that allows you to fool the game into working without actually jumping and such.

Also, if you are involved in white-hat testing and I ban you because of it (seeing suspicious behavior), please let me know in the WB chat room.

21
  • What is already in place to detect cheating? Dec 16, 2020 at 22:07
  • Is my score suspicious? I got 100o honestly, and have no need to cheat at a game :)
    – 10 Rep
    Dec 16, 2020 at 22:25
  • 2
    Would writing a bot to auto-play the game count? Dec 17, 2020 at 1:09
  • 8
    there is a little problem with this post. The game is a reskin of the original T-Rex Google Chrome game. As such, most already documented cheats available in a 5 second Google search work on this version too. And some of the replies seem to have already noticed this. Google-fu bounties, I guess.
    – SPArcheon
    Dec 17, 2020 at 8:51
  • 5
    @sparcheon You are assuming that it is only a reskin and that I left that game logic completely intact. Also, a cheat can be on time score submission, and not touch the game play at all. Dec 17, 2020 at 10:03
  • 2
    No, Yaakov, I am not assuming anything. I am warning you that some of the cheats people found and posted here are findable on Google as easy as searching for "T-rex chrome cheat". What to do with the bounties is up to you.
    – SPArcheon
    Dec 17, 2020 at 10:14
  • Have you already commisioned a white hat from a designer for this winterbash?
    – Luuklag
    Dec 17, 2020 at 19:43
  • 3
    Could hiineedhelp's 6969 in 420.69s be removed? Dec 18, 2020 at 18:30
  • @Zoe I have spent over two hours playing this game, and legitimately got a 2000. If it were removed as cheating I would be more than a little annoyed. Dec 18, 2020 at 18:31
  • 1
    @Redwolf Programs I had all my legitimate scores removed, with the highest being 2455. Hoping to get them restored soon! πŸ˜…
    – Kyle Pollard StaffMod
    Dec 18, 2020 at 18:34
  • 3
    Looking like this game is more like Hack Dash at this point. Sorry, had to make that joke.
    – jrh
    Dec 18, 2020 at 19:11
  • 1
    @collinb there is no heuristic based only on scores. I plan on a bunch of further refinements tomorrow (which will probably reinstate a number of games that have been banned) Dec 19, 2020 at 17:49
  • 1
    @Yaakov Ellis Shouldn't there be a different leaderboard just for cheaters. I mean if somebody is cheating he/she is doing it just for fun. Dec 21, 2020 at 12:32
  • 2
    @Shadowcoder it wont be so interesting. Will be full of games between 50K and 193 million (a score that was amazingly achieved in 13 seconds, although the score of 99,999,999 is arguably even more impressive, since it was achieved in -1 seconds). And then folks will just try to one-up the highest one on it. It is relatively easy to stick in a bogus result (but harder to get the system to recognize it as legitimate). Dec 21, 2020 at 12:35
  • 7
    Launching user-side-validated game for a community of coders sounds like ASKING for trouble. At best, scores won't correlate with gaming skill but coding skill instead. At worst, you'll get hacked via score uploading interface. Good luck... Dec 21, 2020 at 16:53

20 Answers 20

39
+100

Yes, I do!

enter image description here

The process is fairly straight-forward. Went to https://winterbash2020.stackexchange.com/run-with-the-hats directly, and opened the cdn.sstatic.net JS file hd.min.js with the chrome debugger.

(for accessibility reasons, I've included the line numbers in text. Note that these are relative purely to how Chrome felt like beautifying the file, and already varies between FF and Chrome)

First, I added a breakpoint here (line 526 of the previously mentioned JS file):

enter image description here

... although this turned out to be pointless, I changed the starting time. Me and the Chrome debugger fought a little, which probably accounts for the majority of the 67 seconds used. I later notice time exists here, so I might've been able to modify that later. I modified the startedAt time and subtracted 90000, though I doubt it had any effect. This modification was done in the console, using the standard debugging system. Might've forgotten -= when I modified it. /shrug

Secondly, I added a breakpoint here (line 629 of the previously mentioned JS file):

enter image description here

which is where the fun begins. I bumped the distanceRan variable to 9 million just because why not. After fighting with Chrome to get it to continue (it's a bit buggy, possibly a Flatpak artifact, which is entirely my fault, to be clear), the code continued executing and submitted the score.

I tried using the .trigger("gameStarted/gameEnded") function, but it does nothing. Also tried modifying the XHR request (with another breakpoint - courtesy of Firefox's "break on XHR"), but it seems the validation kept me out in spite of hitting both endpoints.

26

Round 2, because this is actually fun :D

Along the same line as the three other answers, here we go again. This is the AFK option, and pretty much only requires time. Ridiculous scores can be achieved.

  1. Open the debugger
  2. Jump to start the game, and while in air, pause execution
  3. Console, this.config.GRAVITY = 0.

The game can now be left in the background to grind score, as setting the gravity to 0 makes the unicorn be released back into the wild.No unicorns were harmed in the testing of this bug. Probably

When satisfied with the score, stop the game again and call this.gameOver(). Due to a bug when GRAVITY is set to 0, the only way to stop the game is manually calling the function. As a bonus, unless you add some type of tamper check, this cannot be checked. The time and score increases as expected, with the only real difference that I don't need to play the game.

If you want my advice, don't accept any scores sent if the console is open. Detecting the console being opened, however, is tricky. Something like this might work on the first try, but as you can see so far, anything put in JS, someone with a console can modify. using the time might work on the first try, but someone could easily set up a breakpoint, modify the time to be under the threshold, and then continue, bypassing this test.

15
  • 6
    Yaakov is going to bed and says to behave ;)
    – Ollie
    Dec 16, 2020 at 23:20
  • 1
    Imma do that too soon, but I got a couple more things I need to test out Dec 16, 2020 at 23:20
  • Me too. First come first served.
    – Ollie
    Dec 16, 2020 at 23:21
  • 3
    Besides, it's not every day I'm told I can break stuff :D Dec 16, 2020 at 23:21
  • 1
    *tells @Zoe to break stuff*
    – Ollie
    Dec 16, 2020 at 23:22
  • I told you there was unicorn racing! Nice trick. Flying unicorns are even better :-)
    – QHarr
    Dec 17, 2020 at 0:23
  • 1
    I'm getting an itch to break stuff again...
    – Ollie
    Dec 17, 2020 at 15:27
  • @Ollie I started this day with one goal: I will get at least one leaderboard position by messing with XHR requests. So yeah :D Dec 17, 2020 at 15:29
  • 1
    @Zoe Looks like you win the award for Most Elegant Cheat Method(s). Good luck winning that bounty!
    – Ollie
    Dec 19, 2020 at 17:33
  • @Ollie what bounty? Dec 19, 2020 at 17:51
  • 1
    @Zoe The bounty Yaakov may or may not award based on the severity of the cheating. First time I've been told I'll get rewarded for cheating severely...
    – Ollie
    Dec 19, 2020 at 17:52
  • @Ollie white hats get paid for hacking, there are even contests. Dec 21, 2020 at 15:22
  • @ShadowWizardisVaccinating Here on Meta? Now I'm getting ideas...
    – Ollie
    Jan 13, 2021 at 21:27
  • @Ollie Not on meta AFAIK. There's contests and sites (tryhackme.com for an instance) built around white hat hacking. Several large companies also give rewards for finding security issues Jan 13, 2021 at 22:02
  • @Zoethe1337Princess I know that, but I was just suggesting ;) Can you imagine?
    – Ollie
    Jan 13, 2021 at 22:03
19
+50

Yes, Forever!

I wrote a script to constantly check if you're at the top of the leaderboard, and if not to submit a score that puts you there.

enter image description here

I imagine this is the same basic approach as others, but all I did was unminify the source code, adapt it slightly, and left it running in a tab.

In order to get around heuristics, I got to the top by setting my desired score, and seconds, to one more than the current leader.

Gist - leave this running, and it'll constantly check to see if you're top of the leaderboard. If you're not, it'll run a game to knock them off the top spot, then revert to checking.

As far as I can tell with the API as designed there's no way to prevent this sort of cheating, however something that might make it a lot harder is sending up a list of jump times (millis since start) - i.e. a timestamp for each press of the spacebar. Much harder to simulate effectively.

5
  • 4
    Imagine what will happen if you leave two scripts running under two different accounts :) Dec 17, 2020 at 20:44
  • Why do you think I'm trying to unlock enough hats with my sock to get the game?
    – fredley
    Dec 17, 2020 at 21:07
  • 2
    can confirm this works. I'll leave mine running and fredley and i can duke it out for the top until we both get banned :)
    – Dragonrage
    Dec 17, 2020 at 23:28
  • 1
    I think I got banned, script no longer putting me on the leaderboard!
    – fredley
    Dec 18, 2020 at 12:34
  • Do I win anything for predicting this outcome last night?
    – Unionhawk
    Dec 18, 2020 at 14:42
16

Round 4! I've wanted to do this since yesterday but couldn't figure out how. And it's not exactly XHR requests, but I'll do you one better.

This game has one gigantic flaw: The obstacles aren't scared of the glorious unicorn! So uh, yeah, I fixed your game for you :p Also heard a rumor cats and unicorns are cooperating.

Several cats joined the unicorns in the fight for unicorn freedom when testing this bug

Unicorn, the obstacle eater!

(this was back when I thought every 200ms was enough, but as the game accelerated, it got too close for comfort, so I bumped it to every 10ms)

var intv2 = setInterval(() => {
    Runner.instance_.horizon.obstacles = []
}, 10);

And when you're ready to win, clearInterval(intv2);

For some reason though, I can't get this method to work if I go too much past 10k points. Not sure why, might be too aggressive invalidation, or you just figured no one is gonna legitimately get that high a score :P

Got this leaderboard entry at least:

enter image description here

Note: for some reason, I can only get this to work from https://winterbash2020.stackexchange.com/run-with-the-hats

Bonus: you can now run in the unicorn fields instead of run over and under hats.

1
  • I wonder if you could also set the dimensions of the hats (or cactus in the code) to 0.
    – Luuklag
    Dec 17, 2020 at 20:25
13
+50

Yep!

Blatant cheating, in screenshot form (picture of me in 2nd and 3rd place with 100k and 50k score, respectively)

I did something similar to what Zoe did, I opened up Chrome's debugger. I started exploring the Runner object and saw that you could directly manipulate distanceRan using the Chrome console. That was what I did for my 2nd place run. But you can't simply set Runner.instance_.distanceRan to whatever you want... If you try, it gets multiplied by 0.025 (setting it to 100,000 gets it reduced down to 2,500) and I was really unsure why... So I did Runner.instance_.distanceRan = 100000 / 0.025 to sort of "undo" that and it seemed to correctly change my score to what I had originally wanted, 100,000.

My second run was done a bit differently. As Zoe pointed out in her answer, there's a gameOver() function that gets called whenever you run into an obstacle. That's cool... So I tried just nulling it out with Runner.instance_.gameOver = null but that just caused the game to crash when I hit an obstacle. So, instead, I set it to a blank function using Runner.instance_.gameOver = function() { /*nothing*/ } which let me continuously run into obstacles without losing.

The problem was, though, that now I couldn't end the game and thus cash out whatever ridiculous score I managed to get as I walked away and grabbed myself a drink. So I copied the contents of the function from the js file and set the gameOver function back to its previous contents and it let me end the game... Brutally, I might add. I was moving so quickly at that point I couldn't switch over and try to save myself. I decided to just set my score to 50k to be on the leaderboard a second time on my next run-through, though.

12
+100

Found a beautiful way to cheat, one you just leave overnight.

Steps to reproduce:

  1. Go to the Run with the Hats page, right-click anywhere and click "Inspect element".
  2. Click the "Console" tab.
  3. Copy and paste this command into the Console and hit Enter: var original = Runner.prototype.gameOver.
  4. Do the same with Step #3, except with this command: Runner.prototype.gameOver = function (){}.
  5. On the next line, f (){} should appear after you press Enter. What I'm pretty sure this does, is replace the gameOver function that is called when the Unicorn hits an obstacle with an empty function (f (){}), so my Unicorn is currently well off the leash.
  6. Copy and paste the command Runner.prototype.gameOver = original into the Console to go back to normal and post your high score (may take a sec to stop).
1
12

Round 3, because I saw you fixed gravity, so I had an intense urge to break it again. import antigravity, am I right? :P

This is a somewhat dynamic system, because it doesn't always behave consistently, for whatever reason. I've gotten in a few scores using it though.

ALL UR LEADERBOARD POSITIONS ARE NOW BELONG TO US

Also had a 30k one, but that one was discarded by the server for some reason. Not entirely sure what happened there, but it might've been down to this method not being properly refined at the time.

Several unicorns were freed in the testing of this bug. #SaveTheUnicorns

Just like the other try, jump to start the game, pause execution, open the console.

this.tRex.config.DROP_VELOCITY = 0
this.tRex.config.GRAVITY = 0
this.tRex.config.INITIAL_JUMP_VELOCITY = -100

In my testing, this has some varied success. In some cases, the unicorn disappears. In other cases, it stops and just doesn't fall back down, and is still in the range of some obstacles. In the event of this, re-pause the game, set DROP_VELOCITY = -1. The window has to remain focus and the website itself cannot be touched, but I got another 2.4k on the leaderboard thanks to that.

After setting a negative DROP_VELOCITY, you might have to rejump to fly away, but it also persists restarting, so failing round 1, it's easy to restart and fly away on round 2.

In the event of the first option (unicorn disappears), the last gravity break step of my previous answer still applies; when happy with the score, pause execution and this.gameOver() in the console. It should stop the game and submit the score.

11
+100

Let's hack the JavaScript itself!

Step 1. Install Resource Override

Works on Microsoft Edge (Chromium-based), too.

Disclaimer: I'm not affiliated with this Chrome extension.

Step 2. Open Microsoft Visual Studio Code (or JetBrains WebStorm if you like)

Fetch a JavaScript file from here: https://cdn.sstatic.net/Winterbash/js/hd.min.js. Save it somewhere.

Recommended: Use a formatter (like VSCode Prettier) on the code for easier navigation.

Disclaimer 2: I'm not affiliated with Microsoft VSCode or JB WS, nor am I affiliated with the VSCode Prettier extension.

Step 3. Edit the JavaScript file you downloaded from Step 2

Assume you prettified the file, the following code should lie on line 126:

function s(n, t, i, r, u, f, e)

Go to this function (Pro Tip: Search for s( with match whole words turned on), and look for the following statement:

this.xPos = r.WIDTH + (e || 0);

Change (e || 0) to 0 and save the file.

Step 4. Open Resource Override

Colloquially, I'll refer to this extension as RO hereafter.

  • Click Add Rule to the right

  • Select URL β†’ File, a new line will show under

  • Enter this into the URL bar:

    https://cdn.sstatic.net/Winterbash/js/hd.min.js?*
    
  • Select Edit File to the right of the URL bar, and paste the edited file from Step 3 in. Select Save & Close on the bottom-right.

Step 5. Go to Hat Dash and refresh the page. Start playing

RO will replace the JS with the version you edited. Now there won't be any obstacles.

Step 5.1. Wait for some time until you get a desired score. Switch to anything else than your browser.

This will end the game at the current score. Have fun!

1
  • 2
    This is a cool extension, I've looked for something like this for my job before but came up empty!
    – Kyle Pollard StaffMod
    Dec 19, 2020 at 18:13
8

Even faster way. Copy all the steps from my other cheat method, but this time, enter another command: Runner.instance_.setSpeed(10000). It cranks the Unicorn's speed way, way up. It will leap off the line real quick though, so you need to press the up arrow to get it rolling.

For those interested in seeing Uni rear-end the obstacles, simply add this line: Runner.instance_.setSpeed(-1).

8
+100

Note: with the new anti-cheating heuristics, this answer no longer works. See this one for a working method.


TL;DR:

(async () => {
    Runner.gameStarted();
    Runner.setCurrentScore(someScore);
    await new Promise(resolve => setTimeout(resolve, seconds * 1000));
    Runner.gameEnded();
})();

Replace someScore and seconds accordingly.


Do this by hand

This is the method with which I cheated, later I realised, it's exactly the same as the piece of code above.

First, I observed that some POST requests are done when the game starts and ends: /hat-dash/start and /hat-dash/end.

  • The first request sends a parameter startedAt which is a time (millis since Epoch). The response is an auth key: {"auth":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}.

  • The second request has multiple parameters:

    • startedAt: a time, it should be the same as the previous request.
    • score: total points of the game.
    • authKey: the auth key you got in the previous request.
    • durationMs: the duration of the game in milliseconds.
    • many histKeys[] parameters which hold previous auth keys including the latest one, probably obtained from localStorage. If you want to see yours, run localStorage.getItem('Game_AuthKeys').split('|');.

Then it was pretty easy:

  1. Play a short game, so that these requests are done.

  2. Go to the Network tab β†’ right-click on the latest /hat-dash/start request β†’ Copy β†’ Copy as fetch. Paste that in the console and change "body": "startedAt=xxxxxxx" to "body": "startedAt=" + new Date().getTime().

  3. Wait for a few seconds, so that the score you'll enter afterwards looks real and Yaakov doesn't notice πŸ˜‡.

  4. Next, copy the latest /hat-dash/end request and:

    • Get the startedAt from the previous /hat-dash/start request and replace it with the date you got previously (Network β†’ Request β†’ scroll until you see Form Data).
    • Replace the score with the one you want.
    • Replace authKey with the value you got from the response of the previous request.
    • Change ...&durationMs=xxxxx... to ...&durationMs=" + (new Date().getTime() - previousStartedAt) + "....
    • Leave the rest of histKeys[] unchanged, only append &histKeys%5B%5D=newestAuthKey to the string.
6
  • I've tried getting this to work, but it looks like there's a lot of filtering done. A few seconds isn't enough depending on your score. It's not really related to a ban, but rather individual filtering. Some leaderboard entries are just silently discarded based on Some Metric:tm:. Also tried this with a setTimeout and simulating a game by copying numbers from the current first place (that I'm still sus is fake). Copied the entire methods though, and added some intermediate variables Dec 17, 2020 at 16:15
  • @Zoe I think it's a ban because I do not appear in the Most Time Spent Playing leaderboard although I've played 131 games in 57 mins. Dec 17, 2020 at 16:18
  • Yaakov said the ban would be obvious though. Also, I'm on several positions of the leaderboard at the moment, but I don't have a time to match my attempts. Invalid times are discarded, but I'm somewhat suspicious it's over-sensitive Dec 17, 2020 at 16:19
  • @Zoe edited with some code. I have not tested it without the timeout, though. Dec 17, 2020 at 16:55
  • Do keep looking into this though. Would love to see something like this give a leaderboard position ^^ Dec 17, 2020 at 16:56
  • Ask for an unban in the room linked in the question. There's also a whitehat removal button coming soon that'll let us remove scores from ourselves without consequences, for further testing Dec 17, 2020 at 17:03
6
+50

I'm not sure if you consider using a tablet with a keyboard cheating or just a clever combination of mechanics, but I thought I'd link it just in case:

https://meta.stackexchange.com/a/358130/718314

Playing the game on a touchscreen with a keyboard makes the game a little easier since the flying down arrows get configured to be easier.

(If this is cheating, don't wipe my scores! I only discovered it in the last half hour, so only my most recent runs were done with this πŸ˜… None of them made the all time leaderboard)

1
  • 5
    Not cheating. Just clever. Dec 17, 2020 at 6:01
5

Rather than editing Javascript, why not send POST requests? (Copy and paste into your browser's console)

var startedAtNow = Date.now();
var authkey = "";
var n = {};
$.ajax({
    type: "POST",
    url: "hat-dash/start",
    data: {
        startedAt: Date.now()
    },
    dataType: "json",
    success: function(t) {
        authkey = t.auth
    },
    error: function(n) {
        console.log("error starting game");
        console.log(n)
    },
    done: function() {
        this.pendingGameStart = null
    }
})
function doend() {
  $.ajax({
      type: "POST",
      url: "hat-dash/end",
      data: {
          startedAt: startedAtNow,
          score: Number(6942069420), // You can change this
          authKey: authkey,
          durationMs: 12345, // You can change this
          histKeys: localStorage.getItem("Game_AuthKeys").split("|")
      },
      dataType: "html",
      success: function(t) {
          authkey = null;
          n.pendingGameEnd = !1;
          n.pendingGameStartedAt && ($.ajax({
                                      type: "POST",
                                      url: "hat-dash/start",
                                      data: {
                                          startedAt: Date.now()
                                      },
                                      dataType: "json",
                                      success: function(t) {
                                          authkey = t.auth
                                      },
                                      error: function(n) {
                                          console.log("error starting game");
                                          console.log(n)
                                      },
                                      done: function() {
                                          this.pendingGameStart = null
                                      }
                                  }))();
          $(".js-stats-data").remove();
          $(".js-personal-stats").append(localStorage.getItem("Game_AuthKeys"))
      },
      error: function(n) {
          console.log("error ending game");
          console.log(n)
      }
  })
}
setTimeout(doend, 12345); // 12345 should be >= durationMs

I just went through the source code and found. The comments explain what you should change. I'd recommend you to set a realistic time for the score.

Basically, score: Number(x) is the score you get, x being the score you want. durationMs: y is how long you want the run to be, y being the number of milliseconds. setTimeout(doend, z) should wait a few more milliseconds than durationMs to simulate the fact that the run actually occurred in real time. The rest of the code was take from the actual javascript. It just takes the data and sends it to the Stack Exchange servers through jQuery AJAX POST.

Proof:

proof that it works

4
  • 1
    Please tell what exactly it does, e.g. what score you got as result, or how it helps you get better score. Dec 18, 2020 at 16:03
  • How is that substantially different from my answer above? Dec 18, 2020 at 17:34
  • @double-beep I hadn't actually realized your answer was there, I must've skipped it since it was so long. My answer is basically just what you said but I did all the work Dec 18, 2020 at 18:16
  • When I tested this, I got some bogus text instead. Must've done something wrong. Dec 24, 2020 at 7:51
5
+50

Thanks letting us have fun :-)

Here's my approach:

const origGameOver = Runner.prototype.gameOver;
Runner.prototype.gameOver = function() {
    if (this.runningTime > 133700) {
        this.lastSurrentScoreText = "9001";
        this.currentStartedAt = Date.now() - 133700;
        return origGameOver.call(this);
    }
}

I keep colliding (accidentally, of course!) with the obstacles, but the game won't end until I played for 133.7 seconds. Otherwise I'm playing normally, with the original code in place to collect the keypresses and send the standard requests.

As obstacles are generated randomly on the client, you won't have a chance to detect this on the server side.

Unfortunately during the first tests I had not yet figured out how to properly have the expected time sent to the server correctly (showing up as 134-something seconds), but didn't follow up on it to avoid messing with the highscore too much.

1
4

You can also use this command:

Runner.instance_.distanceRan = 100000 / Runner.instance_.distanceMeter.config.COEFFICIENT

To start the Unicorn from a specific score, in this case, 100 grand.

5
  • And can you use this right now to get on the leaderboard? Dec 16, 2020 at 23:45
  • @YaakovEllis It looks like it, but I only saw it on the leaderboard for ten minutes or so, must be your new code.
    – Ollie
    Dec 16, 2020 at 23:54
  • @YaakovEllis Another thing that's funny about that: My (cheated) score has a . for the time, not a colon. Is that related?
    – Ollie
    Dec 16, 2020 at 23:55
  • @Ollie that's a valid way to format dates. There's ridiculously many ways dates are formatted, all of which are valid :') Dec 16, 2020 at 23:56
  • 1
    Most unfortunately, none of these methods appear to work now. Yay me for reporting them and yay Yaakov for fixing 'em.
    – Ollie
    Dec 16, 2020 at 23:58
4

First I modified the anti-cheat code in function l() to request the wrong url (/bam-me...) so that I didn't get banned if I made a mistake.

Then, I changed the code to end the game when colliding (f ? (this.gameOver(), this.crashed || l('rewrote gameOver function')) : ...) to change the condition to f && !1 - this means the game doesn't quit if I hit an obstacle.

Next, I played the game as normal to prevent the server-side analytics noticing me. When I wanted to die, I hit alt-right to leave the tab, which calls gameOver() (because focus was lost) and tells the server I died.

Of course, all of the above required me to get custom JS into the page. To avoid potential issues around reloading event handlers, I used the FF extension 'Request Injector' which allowed me to change the response for any request. I could, of course, have used a local file to modify the HTML, but that broke all the relative URLs, so the assets used in the page didn't load and I couldn't see the unicorn :'(. And using a local JS file in a remote HTML fails due to file:/// URL protections, so I used del.dog to host the new JS code.

My suggestion to block this is to add an integrity=... attribute in the <script src="sstatic..."> tag in the HTML, which will at least mean people have to go to the effort of find-replacing all the relative URLs to modify the JS.

The server could also block all requests to access /run-with-the-hats unless they have the correct Referrer, and a client-side check for that could be added to the JS too by document.referrer (this is useful so you have to put the maximum number of pages into your request interceptor, making it more annoying to modify the JS) Me, cheating

3

Just right now, I accidentally changed the browser tab right after pressing Space. I rejoined and the unicorn was gone. But the game runs and gives sounds.

In Google Chrome, on WindowsΒ 10.

You can see it here: https://youtu.be/8MZrcbZttHY

4
  • 2
    Where you able to end the game and submit the score? Dec 18, 2020 at 22:04
  • that will always remain my secret
    – user906393
    Dec 18, 2020 at 22:13
  • 3
    If you weren't able to submit, then it's like you didn't cheat and it's not really an interesting answer for this question ^^ Dec 18, 2020 at 22:19
  • 1
    tab out, tab in again
    – user906393
    Dec 18, 2020 at 22:42
3

I think I shouldn't write this as an answer, but I didn't find anywhere else to write it. 2 days ago I cheated with changing jump speed (it's fixed now). But my score is on the leaderboard:

1

I just want it removed

1
  • 3
    It is removed, thanks for your honesty Dec 22, 2020 at 8:18
1

Found a simple way to cheat that doesn't require any coding.

  1. Open the game: Run with the hats
  2. Start playing and after a while, zoom in/out the page. This causes the game to stop
  3. Wait for some time and then press space to resume the game. Once the game resume you score will be higher. (The more you wait the higher it is)
4
  • Have you gotten a high score doing this - I think that this will still be detected Jan 4, 2021 at 19:29
  • No, I didn't get any scores on the leaderboard
    – l-l
    Jan 4, 2021 at 19:31
  • Well then it is not a successful cheat then. Jan 4, 2021 at 19:40
  • Actually none of my scores show up in the leaderboard even if I don't use the cheat. I wasn't sure how often it updates.
    – l-l
    Jan 4, 2021 at 19:48
1

Yes! I was able to remove the default event listeners and objects, and overwrite the client-side Javascript with a modified version. The modified version has an added line:

n.livesLeft = 1000;

(where the value can be set somewhat arbitrarily) which I put (again, somewhat arbitrarily) after

n.defaultDimensions = {
  WIDTH: y,
  HEIGHT: 150
};

and replaced the collision detection logic

  if (f && ut(f, a, v), y) return [a, v]

with

  if (f && ut(f, a, v), y)  {
    n.livesLeft--;
    if(n.livesLeft<=0) {
        return [a,v];
    }
  }

This mostly works, but eventually the game hits a bug. Specifically, in the update function, there is a line

this.currentSpeed < this.config.MAX_SPEED && (this.currentSpeed += this.config.ACCELERATION))

Because the difference between the starting and max speed is not an exact multiple of the default ACCELERATION parameter, this can lead to a speed exceeding the maximum. Then, when jumping, the startJump() function checks the current speed against MAX_SPEED and triggers an autoban for jumping too fast. The fix for this, which should be in the original, is to change that segment to

(this.currentSpeed + this.config.ACCELERATION) < this.config.MAX_SPEED && (this.currentSpeed += this.config.ACCELERATION))

While it is possible to neutralize the autoban code in the script modifications, doing so should not strictly be necessary, especially after the bugfix.

With this modification, you do still have to play the game, and your end score will be related to how well you play. The difference is just that it's not game over on the first collision; it's quite forgiving with how many collisions you can have before game over.

I'm not sure how this technique could be readily defended against with the present architecture and amount of information sent to the server.

Sometimes, the server-side validation code does flag the game as suspect, but it might just be in the "too good" general category, or it might be in comparing the number of jumps to the expected number of obstacles. It would take some experimentation or a look at server-side code to figure out just what the right jump frequency is, and then modify the code which sends the jump counts at the checkpoint and end of game. I was going to do that piece before finishing this post, but a sufficient number of weeks and the remainder of Winter Bash have gone by without finding time to do that, so I'll skip that final step which could make it go from working some of the time to all of the time. I think it still satisfies what the post was looking for.

2
  • Did you get in the highscore though?
    – Bergi
    Jan 5, 2021 at 21:37
  • I scored high enough to make it there, but not until after having been autobanned from the leaderboard for the bug mentioned.
    – WBT
    Jan 5, 2021 at 21:40
-1

I can change the score by editing the Number function. When it is replaced, the code tries to convert the score string to a number on line 1168 of Firefox's pretty-printed code and runs the custom function instead. Paste this code into the JavaScript console:

function Number(n) {
  return 1000000;
}
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .