Command Reference

Every ! / !! chat command in Banshee's manual, sorted, with the code behind it — the 68 Session Saver commands and the 112 regular commands.

This page catalogs the two command sections of BansheeFunctions.txt[68] Session Saver Commands and [112] Regular Commands — and pairs each with its actual implementation in public/script.js (and, for Session commands, the receiving switch in zombsSessions.js). For the mechanics of how the chat box becomes a command line, see the Chat Commands page; this is the exhaustive lookup. Keybind toggles ([28]) are documented separately on Keybinds & Toggles.

How dispatch works (in one screen)

Every command flows through the same interceptor: Banshee replaces game.network.sendRpc so that any chat message starting with ! is split on spaces and switched on its first token, with args[1] doubling as a Socket lookup. Commands fall into three implementation shapes, and knowing which shape a command is tells you exactly what its code looks like:

ShapeWhat the code doesExample
Local flag flipSets this.scripts.X = true/false (optionally reads an HP/tier arg first); the main tick loop acts on it!mah, !muth, !as
Session relayOnly fires when attached to a Session (user.connectedToId); sends a short e…/d… code that zombsSessions.js turns into a flag flip on the headless Bot!ab, !uth, !wb
Direct actionSends a game packet / spawns an Alt / echoes info to chat immediately, with no persistent flag!send, !respawn, !ft
script.js — the dispatch skeleton (recap)
onSendRpc(e) {
    if (!game.world.inWorld) return;
    if (e.name === "SendChatMessage" && e.message.startsWith("!")) {
        const msg = e.message.toLowerCase();
        const args = msg.split(" ");
        const alt = sockets[args[1]];          // args[1] = optional Socket id
        switch (msg.split(" ")[0].replaceAll(" ", "")) {
            // … every command below is a case in this switch
        }
        return;
    }
    // non-! chat falls through to the real sendRpc
}

[68] Session Saver Commands — the e/d relay

"Sessions only. The Session Saver is what hosts your Sessions." The large majority of Session commands are pure relay commands: they check user.connectedToId and forward a two-letter-prefixed code (e = enable, d = disable) to the server. They do nothing if you aren't attached to a Session. Here's the canonical pair — every relay command is this exact shape with a different code:

script.js — the relay pattern (Auto Build shown; all others identical in shape)
case "!ab":
    if (user.connectedToId) { user.sendMessage("eab"); }
    break;
case "!!ab":
    if (user.connectedToId) { user.sendMessage("dab"); }
    break;

On the server, zombsSessions.js switches on that code and flips the matching flag on the attached Bot's scripts object (see Session Saver — command relay for the receiving code). The full mapping:

CommandName (manual)Wire codesServer-side effect & how it works
!abAuto Buildeab/dabRebuilds the Session's current base — eab seeds a fresh rebuilder map from every owned building's stash-relative grid offset
!auAuto Upgradeeau/dauRe-upgrades the base to its current tiers; seeds the reupgrader map the same way
!atbAuto Boweatb/datbbot.scripts.autobow = true/false — auto-attacks with the bow
!aaAuto Aimeaa/daabot.scripts.autoaim — aimbot toward players
!aprAuto Revive Peteapr/daprbot.scripts.autopetrevive
!apeAuto Evolve Peteape/dapebot.scripts.autopetevolve
!aphAuto Pet Healeaph/daphbot.scripts.autopetheal — heals the pet below 70% HP
!aazAuto Aim Zombieseaaz/daazbot.scripts.autoaimzombies
!aadAuto Aim Demonseaad/daadbot.scripts.autoaimdemons
!ptPlayer Trickept/dptSets playertrick + captures playerTrickPsk — pulls four players' worth of zombie spawns onto one Session for score
!rptReverse Player Trickerpt/drptThe inverse — spreads one player's spawns to four (for zombies)
!brptBoss Reverseebrpt/dbrptReverse trick for bosses — one boss for all four party members
!trptToken Reverseetrpt/dtrptPlayer trick for bosses across multiple Sessions — up to 400 tokens/Session
!ahrcAHRCeahrc/dahrcbot.scripts.ahrc — runs the harvester deposit/collect schedule (see AHRC)
!uaUpgrade Alleua/duabot.scripts.upgradeall
!saSell Allesa/dsabot.scripts.sellall
!uthUpgrade Tower Healtheuth/duthbot.scripts.uth — upgrades buildings below 30% HP to tank damage
!thTower Healeth/dthbot.scripts.towerheal — casts heal spell on buildings below 30% HP
!atAuto Timeouteat/datbot.scripts.autotimeout; alternate across Sessions for AITO
!plPosition Lockepl/dplbot.scripts.positionlock — sticks to a set point
!pfAuto Followepf/dpfbot.scripts.follow — base-defense follow near the stash
!aarAnti Arrow Raideaar/daarbot.scripts.antiarrow — stops attacking so enemy Arrow Towers can't aggro
!revReverterev/drevbot.scripts.revert — sells UTH-upgraded towers back to original tier when safe
!ritReturn Itemserit/dritbot.scripts.returnitems — re-buys set items after dying
!awsAuto Weapon Switcheaws/dawsbot.scripts.autoweaponswitch — distance-based Bow/Bomb/Spear (see tick loop)
!atmAuto Moveeatm/datmbot.scripts.automove — sticks to the second lock position
!alAim Lockeal/dalbot.scripts.aimlock — forces a fixed aim direction
!wbWall Bounceewb/dwbbot.scripts.wallbounce — oscillates between two wall locks for score
!aczAuto Clear Zombieseacz/daczbot.scripts.clearzombies — timed Spear/Bow tier-switching to mop up a wave
!aslAuto Selleasl/daslbot.scripts.autosell — grants sell perms (party-wide when on, Sessions-only when off)

Session actions — fire-once commands (no e/d)

A second group of Session commands aren't toggles — they're one-shot actions that capture a position or trigger a one-time behavior. Their relay code has no e/d prefix; the bare verb is sent as-is:

script.js — fire-once Session actions
case "!lock":   if (user.connectedToId) { user.sendMessage("lock"); }   break;
case "!2lock":  if (user.connectedToId) { user.sendMessage("2lock"); }  break;
case "!alock":  if (user.connectedToId) { user.sendMessage("alock"); }  break;
case "!wlock1": if (user.connectedToId) { user.sendMessage("wlock1"); } break;
case "!wlock2": if (user.connectedToId) { user.sendMessage("wlock2"); } break;
case "!hor":    if (user.connectedToId) { user.sendMessage("hor"); }    break;
case "!ver":    if (user.connectedToId) { user.sendMessage("ver"); }    break;
case "!uptime": if (user.connectedToId) { user.sendMessage("uptime"); } break;
CommandNameHow it works
!lockLock PositionCaptures the Session's current position as the point it should stick to (used by !pl)
!2lockSecond Lock PositionCaptures a second position — the safe spot used by !atm against Arrow Raid
!alockLock AimCaptures the current aim angle as the fixed direction used by !al
!wlock1 / !wlock2Wall Lock 1 / 2Set the two endpoints (slow-trap positions) the Session bounces between for !wb
!hor / !verHorizontal / Vertical Auto MoveChoose the axis the Session oscillates along between the two wall locks
!uptimeSession UptimeAsks the Session how long it's been in the server; result shows in the Statistics tab

[112] Regular Commands — player/socket toggles

"Tabs / Sockets only." These run locally in the browser, almost always by setting a flag on this.scripts that the main player tick loop and every Alt read. The telling detail is that several read an optional argument first — an HP threshold or a tier — before enabling:

script.js — toggles that capture an optional argument
case "!mah":                              // Auto Heal — optional HP%
    if (args[1]) { this.healHealth = args[1]; }
    this.scripts.autoheal = true;
    break;
case "!muth":                             // UTH — optional HP%
    if (args[1]) { this.uthHealth = args[1]; }
    this.scripts.uth = true;
    break;
case "!as":                               // Auto Spear — buy up to a tier
    if (args[1]) { this.spearTier = args[1]; }
    this.requiredGold = this.goldCosts[this.spearTier - 1];
    this.scripts.autospear = true;
    break;
case "!xk":                               // X Key — optional weapon name
    if (args[1]) {
        let weapon = args[1][0].toUpperCase();
        for (let i = 1; i < args[1].length; i++) { weapon += args[1][i]; }
        this.xKeyWeapon = weapon;
    }
    this.scripts.xkey = true;
    break;

!as is a neat example of how the arg feeds the logic: setting spearTier immediately recomputes requiredGold from a goldCosts lookup table, so the auto-spear loop knows exactly how much gold each Socket must bank before it stops buying. !xk title-cases its argument so !xk bow becomes the inventory key "Bow". Every regular toggle:

CommandNameFlag / effect & how it works
!mah [HP]Auto Healscripts.autoheal=true, optional healHealth (default 20%) — buys/equips a HealthPotion below threshold
!mappAuto Pet Potionscripts.autopetpotion — keeps a PetHealthPotion bought
!maph [HP]Auto Pet Healscripts.autopetheal, optional petHealHealth (default 70%)
!mapeAuto Evolve Petscripts.autoevolvepets — token-gated pet evolution ladder (see Pets)
!msaSell Allscripts.sellall — sells every building
!muth [HP]UTHscripts.uth, optional uthHealth (default 30%)
!mth [HP]Tower Healscripts.towerheal, optional towerHealHealth (default 30%)
!matAuto Timeoutscripts.autotimeout — buys Pause/Timeout for player + Sockets
!maaAuto Aimscripts.autoaim — player-side aimbot
!arAuto Respawnscripts.autorespawn — respawns dead Sockets
!af [1/0] [1/0]Auto Refillerscripts.autorefiller + dayFiller/nightFiller args — spawns Alts to keep the server at 40
!arcAuto Reconnectscripts.autoreconnect — re-spawns Sockets that disconnect (Alt lifecycle)
!aajAuto Alt Joinscripts.autoaltjoin — Sockets with a base join your party (clears their bases)
!as [tier]Auto Spearscripts.autospear + spearTier/requiredGold
!spChat Spamscripts.chatspam; also relays esp/dsp to a Session if attached
!xk [item]X Keyscripts.xkey + xKeyWeapon — respawn-raid loop; suppresses Auto Heal
!azsAuto Shieldscripts.autoshield — buys a Tier-1 ZombieShield
!slScore Loggerscripts.scorelogger via setScriptToggle — logs top-4 score per wave
!ssl1P Score Loggerscripts.singlescorelogger — logs your own per-wave score to chat
!dpsDisable Popupswindow.disablepopups — suppresses the hint popups (handy under Auto Build)
!chat [type]Chat VisibilitySets chatVisibility to all/party/spam/none

Two special cases for AHRC and base recording bridge into other pages. !pahrc [alt] sets ahrc on a specific Socket when args[1] resolves to one, otherwise globally; and !record/!arb/!aru/!delete manage saved base layouts in localStorage — both are dissected with full code on the Chat Commands page.

Socket fleet control

These manage the in-browser Alt fleet directly — spawning, closing, renaming, re-indexing, and routing party/equip actions to specific Sockets via the args[1] lookup.

script.js — spawn, polish (re-index), close, reset
case "!send":                              // spawn up to 5 Alts
    const amt = args[1] > 5 ? 5 : args[1];
    for (let i = 0; i < amt; i++) { new Alt(); }
    break;
case "!p":                                 // "polish" — renumber Sockets 1..n
    const alts = Object.values(sockets);
    sockets = {};
    for (let i = 0; i < alts.length; i++) {
        alts[i].id = i + 1;
        sockets[i + 1] = alts[i];
    }
    break;
case "!close":                             // close one Socket by id
    const altId = msg.split(" ")[1] - "";
    if (altId) sockets[altId].ws.send(0);
    break;
case "!reset":                             // close every Socket
    for (let i in sockets) {
        if (sockets[i].myPlayer) { sockets[i].ws.send(0); }
    }
    break;

!p ("Polish Alts") is worth calling out: Sockets get arbitrary incrementing ids as they're created and destroyed, so after a chaotic session the ids are full of gaps. !p rebuilds the whole sockets map from scratch, reassigning ids 1..n in order — which is what makes targeted commands like !altjoin 3 or !l 5 predictable again. Closing a Socket is just ws.send(0) — a zero byte the game treats as a disconnect, which then triggers the Alt's own onClose cleanup.

script.js — per-Socket party & state commands (args[1] = target Socket)
case "!altjoin":                           // a Socket joins you (or another Socket)
    if (!alt) return;
    if (alt.gs) {                          // it owns a base → confirm first
        game.ui.components.PopupOverlay.showConfirmation(`Are you sure you want ${alt.id} to join you? It has a base.`, 10000, () => {
            alt.sendPacket(9, { name: "JoinPartyByShareKey", partyShareKey: game.ui.playerPartyShareKey });
        });
    } else {
        alt.sendPacket(9, { name: "JoinPartyByShareKey", partyShareKey: game.ui.playerPartyShareKey });
    }
    break;
case "!leave":                             // a Socket leaves its party
    if (!alt) return;
    alt.sendPacket(9, { name: "LeaveParty" });
    break;
case "!l":                                 // lock a Socket's position (or all)
    if (alt) { alt.isLocked = true; }
    else { for (let i in sockets) if (sockets[i].myPlayer) sockets[i].isLocked = true; }
    break;
case "!f":                                 // freeze a Socket's movement (or all)
    if (alt) { alt.isFrozen = true; alt.sendPacket(3, { up: 0, down: 0, left: 0, right: 0 }); }
    else { for (let i in sockets) if (sockets[i].myPlayer) { sockets[i].isFrozen = true; sockets[i].sendPacket(3, { up:0,down:0,left:0,right:0 }); } }
    break;

Note the safety rail on !altjoin: if the target Socket owns a gold stash (alt.gs), Banshee pops a 10-second confirmation before joining, because joining your party would abandon/clear that base. isLocked and isFrozen are the same flags the Alt's tick loop checks to ignore the shared cursor — which is why the manual notes locked Sockets "stay locked even with the [P] Key."

CommandNameHow it works
!send [n]Send Alt/sSpawns min(n,5) new Alt() instances
!r [alt]Respawn Alt/sSends { respawn: 1 } to one Socket, or all if no id given
!respawnRespawnRespawns your player via game.network.sendPacket(3, { respawn: 1 })
!an [name]Alt NameRewrites this.altName (blank if no arg) — flows into the Socket Server's opcode-5 message
!join [psk/alt]Join PSK/AltA 20-char arg joins that party key directly; otherwise joins the target Socket's party
!altjoin [alt] [alt]Alt JoinTarget Socket joins you, or (2nd arg) joins another Socket; base-owning Sockets prompt for confirmation
!leave [alt]Leave AltTarget Socket sends LeaveParty
!l [alt] / !!lLock Alt/sSets isLocked so the Socket holds position regardless of the cursor
!f [alt] / !!fFreeze Alt/sSets isFrozen and zeroes movement input
!b [tier] / !e [tier]Buy / Equip Item TierBuys/equips the held weapon at a tier; routes through the Session relay (sendBuffer) when attached, else the local socket
!1!01-by-1 … 10-by-10Set nearestAltCount (1–10) — how many Sockets the ./< raid keys act on at once
!serversSocket Server StatsPops a hint per Socket Server showing its reserved alts count
!setpswd [srv] [pass]Set Socket Server PasswordSaves localStorage.password and relays changePassword … to that Socket Server
!resetpswd [srv]Reset Socket Server PasswordRestores the default password and relays it
script.js — !b routes through the Session relay when attached
case "!b":
    const bTier = parseInt(args[1]);
    const bItem = game.ui.playerTick.weaponName;
    if (args[1]) {
        if (user.connectedToId) {
            user.sendBuffer(new Uint8Array(game.network.codec.encode(9, { name: "BuyItem", itemName: bItem, tier: bTier })));
        } else {
            game.network.sendPacket(9, { name: "BuyItem", itemName: bItem, tier: bTier });
        }
    }
    break;

!b/!e show the third dispatch shape cleanly: the same command does different things depending on context. Attached to a Session, it encodes the RPC and ships it through sendBuffer (prefix byte 2) so the headless Bot performs the buy; otherwise it just sends the packet on your own connection.

Recon, scanning & info

The last group is read-only — it mines Banshee's passive entity cache (allEntities), the live leaderboard, the party list, and the Main Menu's Server Scanner, then echoes results into your own chat with game.ui.components.Chat.onMessageReceived or pops a hint. Nothing here sends a game packet (except the scanner movement of !scanspots).

script.js — !s / !ps echo Socket & party stats into chat
case "!s":                                 // Socket stats: gold + wave per Socket
    for (let i in sockets) {
        if (sockets[i].myPlayer) {
            game.ui.components.Chat.onMessageReceived({
                displayName: `${sockets[i].id}`,
                message: `G ${this.counter(sockets[i].myPlayer.gold)}, W ${this.counter(sockets[i].myPlayer.wave)}`,
                uid: sockets[i].uid
            });
        }
    }
    break;
case "!pop":                               // player + socket count, also retitles the tab
    game.ui.components.PopupOverlay.showHint(`${this.players === 1 ? "1 player" : `${this.players} players`} | ${Object.keys(socketsByUid).length === 1 ? "1 socket" : `${Object.keys(socketsByUid).length} sockets`}`);
    break;
CommandNameHow it works
!ftFind Top PlayersScans allEntities for players, cross-refs the leaderboard for rank + base tier, echoes X/Y to chat (full code on Chat Commands)
!fsFind Top StashesEchoes every known tiered gold stash's position + party id to chat
!sSocket StatsPrints each Socket's gold & wave to chat, labeled by its id
!psParty StatsPrints each party member's wood/stone/gold/token from their entity's targetTick
!popPlayer CountPops the player/socket count and rewrites document.title
!sas / !!sasServer SpotsSets window.serverspots; decodes a saved spot-JSON for the current server and injects tree/stone/demon-camp entities into the world
!scanspotsScan Server SpotsRegisters an entity-update handler that walks the player around the map to discover spots
!am / !!amAuto MoveToggles the scanner's auto-walk (only once !scanspots armed the handler)
!stashes / !playersScanner DataCalls scanner.getStashes/getPlayers (the ScannerAPI) for the current server id
script.js — !sas rebuilds world spots from a saved encoding
case "!sas":
    window.serverspots = true;
    if (this.spotId !== game.options.serverId) {
        this.spotId = game.options.serverId;
        if (serverSpots[this.spotId]) {
            const spots = decodeSpotJSON(serverSpots[this.spotId].spotEncoded);
            game.world.spots = spots;
            game.world.toInclude = toInclude;
            for (let i in spots) {
                game.world.createEntity(toInclude(spots[i]));
            }
        }
    }
    break;

!sas ("Server Spots") is the most involved read command: rather than querying the server, it decodes a pre-recorded, compressed list of every farmable spot on the map (spotEncoded) and manually injects those entities into the renderer — letting you see every tree, stone, and demon camp for score-base and ENV planning without the game ever sending them to you.