CONTROLLER · SAFE MODE ACTIVATION

How to Activate Safe Mode Without Accidental Repeated Use

Use an explicit one-time Memory request, confirmation phrase, owned Controller checks, safeMode, safeModeAvailable, safeModeCooldown and upgradeBlocked preflight, disable before activateSafeMode(), save the return code, and verify later state.

Verification statusChinese source article: Reviewed in full · Official docs: Checked — activateSafeMode(), safeMode, safeModeAvailable, safeModeCooldown, upgradeBlocked and return codes · Trigger boundary: Threat detection and the confirmation phrase are project controls; they do not prove activation is strategically correct

VERIFICATION

Evidence and test status

Chinese source article
Reviewed in full
Official docs
Checked — activateSafeMode(), safeMode, safeModeAvailable, safeModeCooldown, upgradeBlocked and return codes
Trigger boundary
Threat detection and the confirmation phrase are project controls; they do not prove activation is strategically correct
Execution boundary
OK schedules activation; Safe Mode duration and available activation changes require later Controller observation
JavaScript syntax
Passed
Offline state review
Passed — visibility, ownership, active state, activation count, cooldown, upgrade block, confirmation and one-time request states
Screeps Console test
Pending
Live activation, same-shard busy state, charge consumption and next-tick Controller test
Pending
Last verified
July 26, 2026

Quick answer

Do not call activateSafeMode() directly from a broad hostile check. Create a one-time request containing the exact room and confirmation phrase, require a visible owned Controller, no active Safe Mode, at least one available activation, no cooldown and no upgrade block, save the before state, set request.enabled = false, call the API once, store the return code, and verify the Controller on a later tick.

Treat Safe Mode as a reviewed defense action

Safe Mode is an important protection mechanism, but it does not select Tower targets, repair defenses, remove hostiles or fix room layout. It also consumes a limited activation. The trigger must therefore be separated from simple visibility of a non-owned Creep.

function shouldRequestSafeMode(input) {
  return Boolean(
    input.criticalStructureThreatened
    && input.defenseResponseUnavailable
    && input.playerApproved
  );
}

These inputs are project policy, not official API facts. This article begins only after the decision to request activation has already been reviewed.

Read Controller state without changing it

function inspectSafeModeState(roomName) {
  const room = typeof roomName === 'string'
    ? Game.rooms[roomName]
    : null;
  const controller = room?.controller || null;

  if (!controller) {
    return {
      roomVisible: Boolean(room),
      controllerFound: false
    };
  }

  return {
    roomVisible: true,
    controllerFound: true,
    roomName: room.name,
    owned: controller.my === true,
    safeMode: controller.safeMode ?? null,
    safeModeAvailable:
      controller.safeModeAvailable ?? null,
    safeModeCooldown:
      controller.safeModeCooldown ?? null,
    upgradeBlocked:
      controller.upgradeBlocked ?? null,
    ticksToDowngrade:
      controller.ticksToDowngrade ?? null
  };
}

This is a read-only Console-safe inspection. It reduces obvious mistakes but cannot replace the method's official return code.

Create an explicit one-time request

Memory.safeModeRequest = {
  enabled: true,
  roomName: 'W1N1',
  confirmation: 'ACTIVATE_SAFE_MODE'
};

The Memory field and phrase are project controls. The phrase prevents an accidental boolean toggle from becoming a real activation, but it does not prove the room strategy is correct.

Build a testable activation plan

function evaluateSafeModeActivation(input) {
  if (input.enabled !== true) {
    return { ready: false, reason: 'disabled' };
  }

  if (input.confirmation !== 'ACTIVATE_SAFE_MODE') {
    return { ready: false, reason: 'confirmation-missing' };
  }

  if (!input.roomVisible || !input.controllerFound) {
    return { ready: false, reason: 'controller-missing' };
  }

  if (!input.owned) {
    return { ready: false, reason: 'not-owner' };
  }

  if (Number.isFinite(input.safeMode) && input.safeMode > 0) {
    return { ready: false, reason: 'already-active' };
  }

  if (
    !Number.isInteger(input.safeModeAvailable)
    || input.safeModeAvailable <= 0
  ) {
    return { ready: false, reason: 'no-activation' };
  }

  if (
    Number.isFinite(input.safeModeCooldown)
    && input.safeModeCooldown > 0
  ) {
    return { ready: false, reason: 'activation-cooldown' };
  }

  if (
    Number.isFinite(input.upgradeBlocked)
    && input.upgradeBlocked > 0
  ) {
    return { ready: false, reason: 'upgrade-blocked' };
  }

  return { ready: true, reason: 'ready' };
}

Other official restrictions can still reject the operation. In particular, same-shard room state is finalized by ERR_BUSY.

Complete Safe Mode request handler

function handleSafeModeRequest() {
  const request = Memory.safeModeRequest;
  if (!request || request.enabled !== true) {
    return { status: 'disabled' };
  }

  const state = inspectSafeModeState(request.roomName);
  const plan = evaluateSafeModeActivation({
    enabled: request.enabled,
    confirmation: request.confirmation,
    ...state
  });

  request.checkedAt = Game.time;
  request.before = state;
  request.status = plan.reason;

  if (!plan.ready) {
    request.enabled = false;
    return { status: plan.reason, state };
  }

  const room = Game.rooms[request.roomName];
  const controller = room.controller;

  request.enabled = false;
  request.status = 'submitted';
  request.submittedAt = Game.time;

  const result = controller.activateSafeMode();
  request.result = result;
  request.resultAt = Game.time;
  request.status = result === OK
    ? 'accepted'
    : 'failed-review-required';

  return {
    status: request.status,
    roomName: room.name,
    result,
    before: state
  };
}
module.exports.loop = function () {
  const outcome = handleSafeModeRequest();

  if (
    outcome.status === 'failed-review-required'
    || outcome.status === 'accepted'
  ) {
    console.log(JSON.stringify({
      type: 'safe-mode-request-result',
      ...outcome
    }));
  }
};

Disable the request before the API call

request.enabled = false;
const result = controller.activateSafeMode();

If the API rejects the call or later code throws, the next tick will not automatically consume an activation after conditions change. Preserve the request object as evidence and require a new reviewed enable action.

Understand the same-shard busy state

function getVisibleActiveSafeModeRooms() {
  return Object.values(Game.rooms)
    .filter(room =>
      room.controller?.my === true
      && Number.isFinite(room.controller.safeMode)
      && room.controller.safeMode > 0
    )
    .map(room => room.name)
    .sort();
}

This diagnostic lists currently visible owned rooms. The API return code remains authoritative for the shard-wide activation restriction.

Verify the next tick

Re-read controller.safeMode and controller.safeModeAvailable, and compare them with the saved before snapshot. Do not replace the observed fields with predicted values in Memory.

Handle return codes

CodeMeaning hereReview
OKActivation scheduledController state later
ERR_NOT_OWNERController not yoursRoom and ownership
ERR_BUSYAnother same-shard room is activeOwned room Safe Mode state
ERR_NOT_ENOUGH_RESOURCESNo activation availablesafeModeAvailable
ERR_TIREDCooldown, block or Controller restrictionCurrent Controller fields and docs

Debugging checklist

  • Separate threat classification from activation.
  • Require exact room and confirmation.
  • Inspect the visible owned Controller.
  • Check current Safe Mode.
  • Check available activations.
  • Check cooldown and upgrade block.
  • Save the before snapshot.
  • Disable before calling.
  • Store the official return code.
  • Verify Controller state later.

Scope and next steps

This guide does not decide threat severity, run Towers, repair Ramparts, spawn defenders, generate Safe Mode charges or coordinate rooms across shards. Continue with Controller downgrade recovery.

Frequently asked questions

Why keep a failed request instead of deleting it?

It preserves the room, tick, before state and return code for review before a human explicitly retries.

Can a precheck guarantee activation?

No. It removes obvious invalid states; the current official return code is the final same-tick result.

Is the confirmation phrase secure authorization?

No. It is only an operational guard inside your Memory workflow.

Official documentation

SOURCE AND SCOPE

Review the source, evidence, or next system

This English guide is rewritten for a focused search intent while preserving the technical scope and verification boundaries of its Chinese source. Live-room evidence is claimed only when the verification record says it exists.