I’m gonna skip the SEO-filler-food-blogger-backstory because there’s a lot to get through here and I want to keep it as short as possible.
There’s a glitch in the original CALL OF DUTY: MODERN WARFARE 3 (MW3) where players can become invincible. Shortly after the game released, the steps to do the glitch circulated on YouTube, but no one knew how or why it worked. This really bothered me. I always wanted to know, but there simply isn’t enough information in performing the glitch. All I could really do was speculate as to what was causing it.
A few years ago, I discovered a modded version of the game that allows you to view and edit a portion of the source code. With some investigation, I finally figured it out.
Before we get into why the glitch occurs, we need to understand how to do it, but before we get into that, let me just explain the game to those who are unfamiliar with it.
CALL OF DUTY (COD) is a first-person shooter (FPS) where the objective is essentially to kill as many players as possible while limiting your own deaths. Most of the gameplay consists of running around as a soldier with a gun and getting into firefights. However, if you get a certain number of kills without dying, you can receive a killstreak reward. Low killstreaks give you things like an unmanned aerial vehicle (UAV) to see enemy positions on the map. Some of the higher killstreaks allow the player to temporarily shoot powerful weapons from hundreds of feet in the air such as the predator missile, chopper gunner, or AC-130.
Another good thing to know is that COD offers different game modes. It isn’t all just running around and killing. Some modes are objective-based such as Capture the Flag, Search and Destroy, and Domination. Search and destroy is a round-based mode where everyone has one life. There is an attacking team and a defending team. The attacking team has to pick up a bomb and plant it in one of two locations on the map. If the bomb goes off, their team wins. The other team is trying to prevent this.
In order to do the invincibility glitch, you need to go into a private match and set the game mode to Search and Destroy. Set lives to unlimited, and enable the bomb carrier class. Before starting the game, make sure you have the predator missile equipped as a killstreak reward.
Once the match starts, spawn on the attacking team and acquire a predator missile. Walk over to where the bomb is, and right before you fully step over it, begin taking out the predator missile. If you get the timing right, the next time you die, you will be both invisible and invincible.
But why does this happen? How does dying cause you to become invincible, and what does the predator missile have to do with it?
To answer these questions, we have to look back at an older installment of COD.
Despite killstreaks being in the franchise since CALL OF DUTY 4: MODERN WARFARE, 2009’s CALL OF DUTY: MODERN WARFARE 2 (MW2) was the first to offer player-controlled killstreaks such as the predator missile, chopper gunner, and AC-130.
While playing COD, you will inevitably die at some point. When you do, the game respawns you shortly thereafter. Since the newly added player-controlled killstreaks act as an alternative player state, the developers had to answer a critical game design question: What happens to the player if they die while controlling a killstreak?
You could just respawn the player, but then their soldier would be standing out in the open for enemies to shoot at. You could also end the killstreak prematurely, but that doesn’t really seem fair considering the player spent all that time working their way up to it. The third and final option would be to delay the respawn until after the killstreak has ended. This is what the developers chose in the end. However, the way it was implemented is unexpected.
Whether it’s because they were afraid of potential bugs, or if it made more sense from a logic design perspective, or if it was simply the easiest path, I don’t know. But what they did was they made it so when the player dies while using a killstreak, they don’t actually die. The player enters a state called faux death. In this state, the player is still technically alive, but they are both invisible and invincible. They remain locked inside of the killstreak until it ends, at which point they are killed and respawned.
I would imagine the game is probably written in something like C++, but the developers used a custom scripting language called Game Script Code (GSC) for most of the multiplayer logic. It seems to be a simplified version of C++, or at the very least, it uses the C family syntax.
Given the nature of game development, GSC allows threaded calls and is fairly event driven. There are three native functions that are used in GSC:
notify, waittill, and endon.
Here are some basic examples:
spawnPlayer()
{
self notify( "spawned" );
}
onPlayerSpawned()
{
for (;;)
{
self waittill( "spawned" );
}
}
Callback_PlayerKilled( attacker )
{
self endon( "spawned" );
}
They can be thought of as this thing happened, wait until this thing happens, and stop if this thing happens, respectively.
Despite the simplicity of the language, there are many functions spread across many files that are all running while a match is in progress. Some of the functions are hundreds of lines long, and some of the files have dozens of functions. From now on, I’ll only show the relevant lines in each function to explain what’s going on behind the scenes.
Returning to MW2 and the faux death, let’s take a look at how this looks in GSC.
Within the player logic file, there is a function called PlayerKilled_internal that runs every time the player dies:
PlayerKilled_internal( eInflictor, attacker, victim, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, isFauxDeath )
{
if ( isFauxDeath )
victim PlayerHide();
}
The last parameter of the function is a boolean called isFauxDeath that depends on whether or not the player was using a killstreak at the time of death. At some point in the function, there’s a test to see if this is true. If it is, PlayerHide is called, turning the player invisible.
Similarly, every time the player takes damage, Callback_PlayerDamage_internal runs:
Callback_PlayerDamage_internal( eInflictor, eAttacker, victim, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime )
{
if ( !isReallyAlive( victim ) )
return;
}
isReallyAlive( player )
{
if ( isAlive( player ) && !isDefined( player.fauxDead ) )
return true;
return false;
}
If the player is faux dead, they cannot take damage.
Okay, so now we know there’s a built-in game mechanic that purposefully makes the player invincible and that we’re probably accessing it somehow in an unintended way when we perform the invincibility glitch. Let’s first take a look at what goes on under the hood when a player uses a killstreak.
The normal life cycle of a predator missile happens in three separate functions:
tryUsePredatorMissile, initRideKillstreak, and missileEyes
All of these functions are nested within each other. That is, at some point in tryUsePredatorMissile, initRideKillstreak is called, and at some point in initRideKillstreak, missileEyes is called.
tryUsePredatorMissile first sets the player attribute usingRemote and then calls initRideKillstreak:
tryUsePredatorMissile( var_0 )
{
maps\mp\_utility::setUsingRemote( "remotemissile" );
maps\mp\killstreaks\_killstreaks::initRideKillstreak();
}
usingRemote is the attribute that determines whether or not isFauxDeath is true.
Within Callback_PlayerDamage_internal, there is a function that checks if the player’s health has reached zero. If it has, PlayerKilled_internal is called and true is passed in for isFauxDeath:
Callback_PlayerDamage_internal( eInflictor, eAttacker, victim, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime )
{
victim finishPlayerDamageWrapper( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, stunFraction );
}
finishPlayerDamageWrapper( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, stunFraction )
{
if ( (self isUsingRemote() ) && (iDamage >= self.health) && !(iDFlags & level.iDFLAGS_STUN) )
{
PlayerKilled_internal( eInflictor, eAttacker, self, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, 0, true );
}
}
The next stage of the predator missile is initRideKillstreak_internal:
initRideKillstreak( var_0 )
{
initRideKillstreak_internal( var_0 );
}
initRideKillstreak_internal( var_0 )
{
common_scripts\utility::waittill_any_timeout( 1.0, "disconnect", "death", "weapon_switch_started" );
}
This stage runs a check and waits one second for one of three events to occur.
If the player disconnects from the match, dies, or presses the button to switch back to their primary weapon, the predator missile is canceled. usingRemote is also reset, meaning the player will not be considered to be using a killstreak, and any death will not be a faux one.
If one second goes by without any of the three events occurring, missileEyes is called. In this function, the player takes control of the predator missile and they can see the map from a bird’s eye view. The player’s camera and controls become linked to the missile and they are temporarily locked out of controlling their soldier on the ground.
MissileEyes( var_0, var_1 )
{
var_0 maps\mp\_utility::clearUsingRemote();
}
At this point, the killstreak can still be exited, but every method is handled gracefully by the game. Disconnecting or changing teams will end the killstreak, but in both cases, usingRemote is reset and the ability to control the streak is disabled. Changing teams also fully kills the player in the process. The only other way out is to use the killstreak until it ends, at which point, usingRemote is once again reset, and the player returns to their body.
When it came to making sure this system wouldn’t break, the developers of MW2 dotted every i and crossed every t.
Another precautionary measure the developers took when designing the player-controlled killstreak system can be found in killstreakUseWaiter:
killstreakUseWaiter()
{
self notify( "killstreakUseWaiter" );
self endon( "killstreakUseWaiter" );
for (;;)
{
self waittill( "weapon_change", var_0 );
killstreakUsePressed();
}
}
First, the function sends out a notify and an endon for itself. This effectively means that any new instance of the function will end all previous ones. This ensures that only one killstreak can be used at a time.
Then, the rest of the function just waits until the use killstreak button is pressed and then killstreakUsePressed figures out which killstreak function needs to be called.
An instance of killstreakUseWaiter is created every time the player spawns in:
onPlayerSpawned()
{
for (;;)
{
self waittill( "spawned_player" );
thread killstreakUseWaiter();
}
}
So given all of the thought that was put into designing these functions and how they interact with each other in MW2, where did things go so horribly wrong in MW3?
During the development of MW2, when these mechanics were being created, the developers did so under the assumption that every time the player respawns, it means they must have died first. Because of this, every check the game makes to safely exit the predator missile relies solely on checking if the player has died, not if they have respawned.
So what would happen if there was a way to respawn without dying?
Quickly, before moving on, I think it’s crucial to understand what was going on with the developers between MW2 and MW3.
During the post-launch development for MW2, there was a scandal involving the studio heads of Infinity Ward and COD’s publishing company, Activision. Basically, they were attempting to switch publishers to Electronic Arts (EA), but then Activision found out and fired the people involved. This caused a bunch of people at Infinity Ward to quit.
In order to keep up with the yearly release schedule of COD, Activision hired the studios Sledgehammer Games and Raven Software to finish MW3.
So essentially what we have is a bunch of developers who had never seen COD’s code base before tasked with working with it and adding features to it with a tight deadline, which is kind of a recipe for disaster.
During the development of MW3, a decision was made to introduce the bomb carrier class feature to the private match version of Search and Destroy.
When a player picks up the bomb, the applyBombCarrierClass function is called:
applyBombCarrierClass()
{
thread maps\mp\gametypes\_playerlogic::spawnPlayer( 1 );
}
The player’s class and loadout are altered temporarily to one configured within the pregame lobby settings:
In order to apply this change immediately, spawnPlayer is called, and the player respawns at their current position.
Now that we have all of pieces, let’s run through the steps of the invincibility glitch one last time.
The player spawns in, creating an instance of killstreakUseWaiter. The value of the player attribute usingRemote starts off as undefined, which for all intents and purposes means false:
onPlayerSpawned()
{
for (;;)
{
self waittill( "spawned_player" );
thread killstreakUseWaiter();
}
}
Once the player earns a predator missile, they press the killstreak button which begins the process of taking it out.
tryUsePredatorMissile is called, and usingRemote is set to ”remotemissile”, which again essentially means true:
tryUsePredatorMissile( var_0 )
{
maps\mp\_utility::setUsingRemote( "remotemissile" );
maps\mp\killstreaks\_killstreaks::initRideKillstreak();
}
We then enter initRideKillstreak and the one second timer begins. If the player disconnects, dies, or presses the switch weapon button, the predator missile is canceled and usingRemote is set to false:
initRideKillstreak_internal( var_0 )
{
common_scripts\utility::waittill_any_timeout( 1.0, "disconnect", "death", "weapon_switch_started" );
}
At this precise moment within the one second interval, the player steps over the bomb, triggering the applyBombCarrierClass function. applyBombCarrierClass sets the new loadout and calls spawnPlayer:
applyBombCarrierClass()
{
thread maps\mp\gametypes\_playerlogic::spawnPlayer( 1 );
}
Every time the player respawns, a new instance of killstreakUseWaiter is called, and the old one is poofed out of existence. Since our predator missile was nested within it, that too is gone:
spawnPlayer( var_0 )
{
self notify( "spawned_player" );
}
onPlayerSpawned()
{
for (;;)
{
self waittill( "spawned_player" );
thread killstreakUseWaiter();
}
}
killstreakUseWaiter()
{
self notify( "killstreakUseWaiter" );
self endon( "killstreakUseWaiter" );
}
Since we have exited the predator missile without triggering any of the three events, usingRemote remains true. The next time the player dies, the game will check usingRemote, see that it is true, and force the player into a faux death. However, because the predator missile was canceled, the player isn’t control locked by the killstreak like they usually are. They are free to run around and shoot at other players.
This glitch is a perfect example of how programs operate on a purely logical level, adhering strictly to the rules as defined, regardless of the intention. And while the intention might be to aim for perfection, a program is always just a reflection of its implementation.
But I think there’s beauty in that. Not only in the singular truth of the system, but in the reminder that behind every line of code, there’s a real person. Someone who’s living their life, making mistakes, and learning from them, just like me.