FOUNDATION · MEMORY AND STATE LIFETIMES

How Screeps Memory Persists State Across Ticks

CURATED PATH

These links are maintained for a concrete prerequisite, failure mode, debugging step, or next lesson instead of generic topic similarity.

CPU / Runtime

Recommended next steps

RawMemory segmentsAdvanced MemoryInterShardMemory syncCross-shard stateGlobal cacheRuntime cache
Verification statusChinese source article: Existing bilingual mapping retained; this English-only pass did not re-review the Chinese source in full or treat it as live-game evidence · Prior verification wording: The legacy “Reviewed in full” phrase is retained only as a compatibility signal; this revision does not claim a new full Chinese-source review · Official documentation: Checked August 17, 2026 — Game is rebuilt each tick, Memory JSON persistence, Creep.memory, StructureSpawn memory initialization, and Game.getObjectById() visibility/null behavior

VERIFICATION

Evidence and test status

Chinese source article
Existing bilingual mapping retained; this English-only pass did not re-review the Chinese source in full or treat it as live-game evidence
Prior verification wording
The legacy “Reviewed in full” phrase is retained only as a compatibility signal; this revision does not claim a new full Chinese-source review
Official documentation
Checked August 17, 2026 — Game is rebuilt each tick, Memory JSON persistence, Creep.memory, StructureSpawn memory initialization, and Game.getObjectById() visibility/null behavior
Static code review
Passed — current-tick, persistent, and disposable heap lifetimes stay distinct; remembered object IDs now preserve remote assignments when the room is not visible instead of treating null as destruction proof
Evidence level
Official-documentation review and static code review only; no real-shard execution is claimed
Console test pending
Not run in this editorial pass
Screeps Console test
Pending — no real-shard Console transcript was collected for this revision
Live multi-tick verification pending
No live global-reset trace, remote-vision-loss trace, or multi-tick Memory persistence transcript was collected for this revision
Last editorial review
August 17, 2026

Use this guide when

Use this guide when a decision must survive later ticks or a global reset and you need to decide whether it belongs in persistent Memory or in disposable heap cache. The three places state can live for this decision are current-tick game state, persistent Memory, and rebuildable JavaScript heap data.

  • Current-tick Game data: read the objects and state that exist now.
  • Persistent Memory: keep correctness-critical JSON-compatible decisions that must survive a runtime reset.
  • Module or global heap data: cache derived information only when losing it on a global reset is safe.

Save IDs and recover the current object with Game.getObjectById() when a target must be remembered. Live multi-tick verification remains pending for this editorial revision; the page does not present a fabricated reset trace.

Quick answer

Game, JavaScript heap data, and Memory have different lifetimes. Game describes the current tick. Module-scope and global values can be reused across ticks while the same runtime context stays alive, but a global reset can remove them. Memory is the JSON-backed store for state that must still be available after that reset.

Use this guide when: you need a Creep to remember a role, phase, home room, target ID, or another small decision across ticks. Choose another guide when: the value is only a rebuildable performance cache or you need large persistent payloads; those belong in the global-cache or RawMemory Segments guides.

Memory, Game, and heap data have different lifetimes

PlaceLifetime to design forGood useFailure boundary
GameCurrent tickVisible Creeps, Structures, Sources, rooms, and current runtime valuesRebuilt for the next tick
Local variable inside loopCurrent loop callTemporary calculationsCreated again on the next call
Module scope / globalMay survive while the runtime context is reusedDerived caches that are cheap enough to rebuildCan disappear on a global reset
MemoryPersistent JSON-backed stateDecisions that must survive later ticks and runtime resetsMust remain serializable and intentionally bounded

This distinction prevents two opposite bugs. Putting every cache into Memory adds unnecessary persistent data and parse/serialization work. Treating heap data as durable state can break the colony after a global reset.

let sourceCache = null;

module.exports.loop = function () {
  let temporaryCount = 0;

  sourceCache ??= {};
  temporaryCount += 1;

  // Memory is the durable choice for a decision
  // that must survive a global reset.
  Memory.colony ??= {};
  Memory.colony.mode ??= 'normal';
};

sourceCache may be reused on later ticks, but production code must be able to reconstruct it when the runtime context is recreated. Memory.colony.mode is appropriate only if that mode is genuinely persistent state.

What Memory actually preserves

The official Global Objects documentation separates Game from Memory: the game object is recreated for each tick, while JSON data written through Memory is serialized and made available on later ticks.

Memory.exampleValue = 1;

console.log(JSON.stringify({
  tick: Game.time,
  value: Memory.exampleValue
}));

That example proves only a write in the current execution. To verify persistence in your own account, inspect the same field on later ticks. This article has not recorded a live multi-tick transcript, so that verification remains pending rather than being presented as observed evidence.

How creep.memory relates to Memory.creeps

A Creep exposes its own persistent memory through creep.memory. For ordinary Creep state, this corresponds to the name-indexed entry under Memory.creeps.

const creep = Game.creeps.Worker1;

if (creep) {
  creep.memory.role = 'harvester';

  console.log(JSON.stringify({
    fromCreep: creep.memory.role,
    fromMemory: Memory.creeps?.[creep.name]?.role
  }));
}

role is not an official Screeps class. It is your own string, and it has no effect until your code reads it and chooses behavior. Keep spelling and schema rules consistent across the modules that consume it.

Initialize Memory when spawning

StructureSpawn.spawnCreep() accepts an options object with a memory field. This is a clean place to initialize Creep state instead of relying on a later tick to guess the intended role.

const result = Game.spawns.Spawn1.spawnCreep(
  [WORK, CARRY, MOVE],
  'Worker1',
  {
    memory: {
      role: 'harvester',
      working: false,
      homeRoom: 'E51S44'
    }
  }
);

console.log('spawnCreep() returned ' + result);

The Memory option does not bypass normal spawn requirements. The Spawn must be yours, the name and body must be valid, the room must have enough available Energy, and the return code must be checked. Use the first spawnCreep() lesson for that action boundary.

Persist a working-state decision

A boolean such as working is useful when the next action depends on a phase that should not reset every tick. Switch at resource boundaries, not on every loop call:

const usedEnergy = creep.store.getUsedCapacity(
  RESOURCE_ENERGY
);
const freeEnergy = creep.store.getFreeCapacity(
  RESOURCE_ENERGY
);

if (usedEnergy === 0) {
  creep.memory.working = false;
} else if (freeEnergy === 0) {
  creep.memory.working = true;
}

At partial Energy, the previous state is preserved. Writing the boolean does not make the Creep harvest, transfer, build, or upgrade automatically; your action logic must read it. The working-state guide covers that state machine in detail.

Choose data that belongs in Memory

CandidateStore in Memory?Reason
'harvester'Usually yesSmall persistent role decision
true / falseUsually yesSmall phase or policy state
'E51S44'Yes when needed laterStable room identifier
Game object ID stringYes when the target must be rememberedLets later code recover the current visible object
Pathfinding result that can be rebuiltUsually heap cache firstDerived data should tolerate reset and avoid unnecessary persistent writes
Unbounded event historyNoIt grows indefinitely and turns diagnostics into persistent overhead
Live Source, Creep, or Structure objectNoThe object belongs to current game state, not durable JSON state

If you store arrays or objects, give them a clear schema and a bound. Persistence is not a reason to retain data forever.

Store object IDs, not live objects

When a Creep needs to remember a Source, store both the stable ID and enough location context to distinguish “not visible” from “visible but unavailable” later:

const source = creep.pos.findClosestByRange(
  FIND_SOURCES_ACTIVE
);

if (source) {
  creep.memory.sourceId = source.id;
  creep.memory.sourceRoom = source.pos.roomName;
}

Game.getObjectById() can access room objects only when their room is visible. A null result therefore does not, by itself, prove that a remembered remote object was destroyed or that its ID is invalid.

function resolveRememberedSource(creep) {
  const sourceId = creep.memory.sourceId;
  const sourceRoom = creep.memory.sourceRoom;

  if (
    typeof sourceId !== 'string'
    || typeof sourceRoom !== 'string'
  ) {
    return { status: 'source-not-assigned', source: null };
  }

  if (!Game.rooms[sourceRoom]) {
    return {
      status: 'source-room-not-visible',
      source: null
    };
  }

  const source = Game.getObjectById(sourceId);
  if (!source) {
    return {
      status: 'source-missing-in-visible-room',
      source: null
    };
  }

  return { status: 'source-visible', source };
}

Keep the assignment when the room is merely out of vision. If the room is visible and a target that your design expects to be persistent is genuinely missing, then clear or repair the assignment according to that target type. Transient targets such as dropped Resources need a different invalidation policy from persistent room objects such as Sources.

Handle Memory after a Creep dies

Game.creeps describes current Creeps. Name-indexed Creep Memory can outlive the Creep, so stale entries may remain after the name disappears from Game.creeps.

for (const name in Memory.creeps) {
  if (!Game.creeps[name]) {
    console.log(name + ' exists in Memory but not Game.creeps');
  }
}

This diagnostic does not delete anything. Use the dead Creep Memory cleanup guide before adding deletion, especially when other name-indexed task structures depend on the same Creep.

Minimal reset-safe example

State impact: this loop initializes two small fields on Worker1. It does not move the Creep or submit a game action.

module.exports.loop = function () {
  const creep = Game.creeps.Worker1;

  if (!creep) {
    console.log('Worker1 was not found.');
    return;
  }

  if (typeof creep.memory.role !== 'string') {
    creep.memory.role = 'harvester';
  }

  if (typeof creep.memory.working !== 'boolean') {
    creep.memory.working = false;
  }

  console.log(JSON.stringify({
    tick: Game.time,
    name: creep.name,
    role: creep.memory.role,
    working: creep.memory.working
  }));
};

To verify it yourself, observe the same Memory fields on later ticks. Then trigger or wait for a real global reset only if you already have a safe way to identify one; the persistent fields should not depend on heap-cache survival. No such live reset trace is claimed by this article.

Production adaptation notes

Keep correctness state small and explicit. A useful production split is:

  • Memory: role, phase, durable task identity, home room, schema version, and other decisions whose loss would change behavior incorrectly.
  • Heap cache: derived lookup tables, parsed configuration, path helpers, or indexes that can be rebuilt after a reset.
  • RawMemory Segments: larger persistent string payloads that need their own activation and validation lifecycle.

Do not duplicate the same truth in several places unless you define which copy is authoritative. If a heap cache is derived from Memory, rebuild from Memory after reset rather than attempting to make both independently persistent.

Debugging checklist

  • Check that the Creep exists before reading creep.memory.
  • Use one spelling and schema for role or phase values.
  • Do not expect a custom Memory field to perform an action by itself.
  • Persist correctness-critical decisions; keep rebuildable derived data in heap cache.
  • Assume module/global cache can vanish after a global reset.
  • Store object IDs instead of live game objects.
  • Handle Game.getObjectById() returning null without deleting a remote assignment until visibility makes that conclusion safe.
  • Bound arrays, histories, and diagnostic data.
  • Do not infer a death cause merely because a name remains in Memory.creeps.

Choose the next guide

Use Disposable Global Cache when the problem is repeated calculation that is safe to rebuild. Use RawMemory Segments when you need larger persistent string storage. Use Working State when you want a Creep to act on a persistent acquire/spend phase.

Frequently asked questions

Does a normal JavaScript variable survive every Screeps tick?

A local variable created inside module.exports.loop is recreated on the next call. Module-scope or global values may survive runtime-context reuse, but they can disappear on a global reset. Use Memory when correctness requires the value after that reset.

Is creep.memory different from Memory.creeps?

creep.memory exposes the Creep-specific persistent Memory associated with that Creep name. It is the convenient place for small per-Creep state.

Can I save a Source or Structure object directly?

Do not treat a live object as durable state. Save its id, resolve it with Game.getObjectById() later, and handle null.

When should I use global cache instead?

Use heap cache for derived data that is safe to lose and deterministic to rebuild. Use Memory when loss after a global reset would make behavior incorrect.

Official documentation

SOURCE AND SCOPE

Review the source, evidence, or next system

This English guide keeps the technical scope and verification boundaries of its Chinese source. The verification record shows which checks were completed and which runtime observations are still pending.