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:
| Shape | What the code does | Example |
|---|---|---|
| Local flag flip | Sets this.scripts.X = true/false (optionally reads an HP/tier arg first); the main tick loop acts on it | !mah, !muth, !as |
| Session relay | Only 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 action | Sends a game packet / spawns an Alt / echoes info to chat immediately, with no persistent flag | !send, !respawn, !ft |
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:
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:
| Command | Name (manual) | Wire codes | Server-side effect & how it works |
|---|---|---|---|
!ab | Auto Build | eab/dab | Rebuilds the Session's current base — eab seeds a fresh rebuilder map from every owned building's stash-relative grid offset |
!au | Auto Upgrade | eau/dau | Re-upgrades the base to its current tiers; seeds the reupgrader map the same way |
!atb | Auto Bow | eatb/datb | bot.scripts.autobow = true/false — auto-attacks with the bow |
!aa | Auto Aim | eaa/daa | bot.scripts.autoaim — aimbot toward players |
!apr | Auto Revive Pet | eapr/dapr | bot.scripts.autopetrevive |
!ape | Auto Evolve Pet | eape/dape | bot.scripts.autopetevolve |
!aph | Auto Pet Heal | eaph/daph | bot.scripts.autopetheal — heals the pet below 70% HP |
!aaz | Auto Aim Zombies | eaaz/daaz | bot.scripts.autoaimzombies |
!aad | Auto Aim Demons | eaad/daad | bot.scripts.autoaimdemons |
!pt | Player Trick | ept/dpt | Sets playertrick + captures playerTrickPsk — pulls four players' worth of zombie spawns onto one Session for score |
!rpt | Reverse Player Trick | erpt/drpt | The inverse — spreads one player's spawns to four (for zombies) |
!brpt | Boss Reverse | ebrpt/dbrpt | Reverse trick for bosses — one boss for all four party members |
!trpt | Token Reverse | etrpt/dtrpt | Player trick for bosses across multiple Sessions — up to 400 tokens/Session |
!ahrc | AHRC | eahrc/dahrc | bot.scripts.ahrc — runs the harvester deposit/collect schedule (see AHRC) |
!ua | Upgrade All | eua/dua | bot.scripts.upgradeall |
!sa | Sell All | esa/dsa | bot.scripts.sellall |
!uth | Upgrade Tower Health | euth/duth | bot.scripts.uth — upgrades buildings below 30% HP to tank damage |
!th | Tower Heal | eth/dth | bot.scripts.towerheal — casts heal spell on buildings below 30% HP |
!at | Auto Timeout | eat/dat | bot.scripts.autotimeout; alternate across Sessions for AITO |
!pl | Position Lock | epl/dpl | bot.scripts.positionlock — sticks to a set point |
!pf | Auto Follow | epf/dpf | bot.scripts.follow — base-defense follow near the stash |
!aar | Anti Arrow Raid | eaar/daar | bot.scripts.antiarrow — stops attacking so enemy Arrow Towers can't aggro |
!rev | Revert | erev/drev | bot.scripts.revert — sells UTH-upgraded towers back to original tier when safe |
!rit | Return Items | erit/drit | bot.scripts.returnitems — re-buys set items after dying |
!aws | Auto Weapon Switch | eaws/daws | bot.scripts.autoweaponswitch — distance-based Bow/Bomb/Spear (see tick loop) |
!atm | Auto Move | eatm/datm | bot.scripts.automove — sticks to the second lock position |
!al | Aim Lock | eal/dal | bot.scripts.aimlock — forces a fixed aim direction |
!wb | Wall Bounce | ewb/dwb | bot.scripts.wallbounce — oscillates between two wall locks for score |
!acz | Auto Clear Zombies | eacz/dacz | bot.scripts.clearzombies — timed Spear/Bow tier-switching to mop up a wave |
!asl | Auto Sell | easl/dasl | bot.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:
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;
| Command | Name | How it works |
|---|---|---|
!lock | Lock Position | Captures the Session's current position as the point it should stick to (used by !pl) |
!2lock | Second Lock Position | Captures a second position — the safe spot used by !atm against Arrow Raid |
!alock | Lock Aim | Captures the current aim angle as the fixed direction used by !al |
!wlock1 / !wlock2 | Wall Lock 1 / 2 | Set the two endpoints (slow-trap positions) the Session bounces between for !wb |
!hor / !ver | Horizontal / Vertical Auto Move | Choose the axis the Session oscillates along between the two wall locks |
!uptime | Session Uptime | Asks 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:
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:
| Command | Name | Flag / effect & how it works |
|---|---|---|
!mah [HP] | Auto Heal | scripts.autoheal=true, optional healHealth (default 20%) — buys/equips a HealthPotion below threshold |
!mapp | Auto Pet Potion | scripts.autopetpotion — keeps a PetHealthPotion bought |
!maph [HP] | Auto Pet Heal | scripts.autopetheal, optional petHealHealth (default 70%) |
!mape | Auto Evolve Pet | scripts.autoevolvepets — token-gated pet evolution ladder (see Pets) |
!msa | Sell All | scripts.sellall — sells every building |
!muth [HP] | UTH | scripts.uth, optional uthHealth (default 30%) |
!mth [HP] | Tower Heal | scripts.towerheal, optional towerHealHealth (default 30%) |
!mat | Auto Timeout | scripts.autotimeout — buys Pause/Timeout for player + Sockets |
!maa | Auto Aim | scripts.autoaim — player-side aimbot |
!ar | Auto Respawn | scripts.autorespawn — respawns dead Sockets |
!af [1/0] [1/0] | Auto Refiller | scripts.autorefiller + dayFiller/nightFiller args — spawns Alts to keep the server at 40 |
!arc | Auto Reconnect | scripts.autoreconnect — re-spawns Sockets that disconnect (Alt lifecycle) |
!aaj | Auto Alt Join | scripts.autoaltjoin — Sockets with a base join your party (clears their bases) |
!as [tier] | Auto Spear | scripts.autospear + spearTier/requiredGold |
!sp | Chat Spam | scripts.chatspam; also relays esp/dsp to a Session if attached |
!xk [item] | X Key | scripts.xkey + xKeyWeapon — respawn-raid loop; suppresses Auto Heal |
!azs | Auto Shield | scripts.autoshield — buys a Tier-1 ZombieShield |
!sl | Score Logger | scripts.scorelogger via setScriptToggle — logs top-4 score per wave |
!ssl | 1P Score Logger | scripts.singlescorelogger — logs your own per-wave score to chat |
!dps | Disable Popups | window.disablepopups — suppresses the hint popups (handy under Auto Build) |
!chat [type] | Chat Visibility | Sets 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.
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.
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."
| Command | Name | How it works |
|---|---|---|
!send [n] | Send Alt/s | Spawns min(n,5) new Alt() instances |
!r [alt] | Respawn Alt/s | Sends { respawn: 1 } to one Socket, or all if no id given |
!respawn | Respawn | Respawns your player via game.network.sendPacket(3, { respawn: 1 }) |
!an [name] | Alt Name | Rewrites this.altName (blank if no arg) — flows into the Socket Server's opcode-5 message |
!join [psk/alt] | Join PSK/Alt | A 20-char arg joins that party key directly; otherwise joins the target Socket's party |
!altjoin [alt] [alt] | Alt Join | Target Socket joins you, or (2nd arg) joins another Socket; base-owning Sockets prompt for confirmation |
!leave [alt] | Leave Alt | Target Socket sends LeaveParty |
!l [alt] / !!l | Lock Alt/s | Sets isLocked so the Socket holds position regardless of the cursor |
!f [alt] / !!f | Freeze Alt/s | Sets isFrozen and zeroes movement input |
!b [tier] / !e [tier] | Buy / Equip Item Tier | Buys/equips the held weapon at a tier; routes through the Session relay (sendBuffer) when attached, else the local socket |
!1 … !0 | 1-by-1 … 10-by-10 | Set nearestAltCount (1–10) — how many Sockets the ./< raid keys act on at once |
!servers | Socket Server Stats | Pops a hint per Socket Server showing its reserved alts count |
!setpswd [srv] [pass] | Set Socket Server Password | Saves localStorage.password and relays changePassword … to that Socket Server |
!resetpswd [srv] | Reset Socket Server Password | Restores the default password and relays it |
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).
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;
| Command | Name | How it works |
|---|---|---|
!ft | Find Top Players | Scans allEntities for players, cross-refs the leaderboard for rank + base tier, echoes X/Y to chat (full code on Chat Commands) |
!fs | Find Top Stashes | Echoes every known tiered gold stash's position + party id to chat |
!s | Socket Stats | Prints each Socket's gold & wave to chat, labeled by its id |
!ps | Party Stats | Prints each party member's wood/stone/gold/token from their entity's targetTick |
!pop | Player Count | Pops the player/socket count and rewrites document.title |
!sas / !!sas | Server Spots | Sets window.serverspots; decodes a saved spot-JSON for the current server and injects tree/stone/demon-camp entities into the world |
!scanspots | Scan Server Spots | Registers an entity-update handler that walks the player around the map to discover spots |
!am / !!am | Auto Move | Toggles the scanner's auto-walk (only once !scanspots armed the handler) |
!stashes / !players | Scanner Data | Calls scanner.getStashes/getPlayers (the ScannerAPI) for the current server id |
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.