DEFENSE · RAMPART INTENT IDENTITY

Change One Rampart Access State and Verify the Exact Object

CURATED PATH

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

Defense / Combat

Recommended next steps

Connect Rampart state to fortification targetsStructure upkeepCheck Safe Mode for emergency defenseEmergency protectionConnect access state back to active defenseDefense execution
Verification statusChinese source article: Reviewed in full · Official engine: Checked — setPublic requires ownership, coerces the argument to a boolean, stores one setPublic intent for the exact Rampart ID and emits no Room event · Same-tick boundary: Checked — another setPublic call for the same Rampart can replace the earlier intent before tick processing

VERIFICATION

Evidence and test status

Chinese source article
Reviewed in full
Official engine
Checked — setPublic requires ownership, coerces the argument to a boolean, stores one setPublic intent for the exact Rampart ID and emits no Room event
Same-tick boundary
Checked — another setPublic call for the same Rampart can replace the earlier intent before tick processing
Static code review
Passed — exact Rampart identity, strict boolean input, one dispatcher reservation, accepted-call pending state and next-tick verification
JavaScript syntax
Passed
Screeps Console test
Pending
Live same-tick overwrite, public/private passage, object replacement and next-tick state verification
Pending
Genuine room or Console screenshots
Pending
Last verified
August 4, 2026

Start with the intent boundary

StructureRampart.setPublic() schedules one boolean state change. In the official engine, the call writes a setPublic intent keyed to the Rampart ID and returns OK. A second call for the same Rampart during the same JavaScript tick can replace the earlier value before the server processes either request.

That makes the method different from a simple configuration assignment. One module may submit true, another may submit false, and both callers may see OK while only the final intent remains. Use one dispatcher, one Rampart reservation and one next-tick verifier.

The method creates no Room event. Visual appearance, a Creep standing nearby or the original return value is not completion evidence.

Bind approval to one exact Rampart

function buildRampartConfirmation(request) {
  return [
    'SET_RAMPART_ACCESS',
    request.requestId,
    request.rampartId,
    request.roomName,
    request.x,
    request.y,
    request.public ? 'PUBLIC' : 'PRIVATE'
  ].join('_');
}
Memory.rampartAccessRequests ??= {};

Memory.rampartAccessRequests['gate-west-1'] = {
  requestId: 'gate-west-1',
  enabled: true,
  rampartId: 'replace-with-rampart-id',
  roomName: 'W1N1',
  x: 20,
  y: 20,
  public: true,
  requestedAt: Game.time,
  confirmation:
    'SET_RAMPART_ACCESS_gate-west-1_' +
    'replace-with-rampart-id_W1N1_20_20_PUBLIC'
};

The confirmation includes the request ID, Rampart ID, room, coordinates and target state. Changing any of those fields invalidates the old approval. Require typeof request.public === 'boolean'; the engine coerces truthy and falsy values, but a production request should not silently turn the string "false" into public access.

Validate current identity and state

function evaluateRampartAccess(request, rampart) {
  if (!request || request.enabled !== true) {
    return { ready: false, status: 'request-disabled' };
  }

  if (
    typeof request.requestId !== 'string'
    || request.requestId.length === 0
    || typeof request.rampartId !== 'string'
    || typeof request.roomName !== 'string'
    || !Number.isInteger(request.x)
    || !Number.isInteger(request.y)
    || request.x < 0
    || request.x > 49
    || request.y < 0
    || request.y > 49
    || typeof request.public !== 'boolean'
    || !Number.isInteger(request.requestedAt)
  ) {
    return { ready: false, status: 'request-invalid' };
  }

  if (request.confirmation !== buildRampartConfirmation(request)) {
    return { ready: false, status: 'confirmation-mismatch' };
  }

  if (!rampart) {
    return { ready: false, status: 'rampart-unavailable' };
  }

  if (
    rampart.id !== request.rampartId
    || rampart.structureType !== STRUCTURE_RAMPART
    || rampart.pos.roomName !== request.roomName
    || rampart.pos.x !== request.x
    || rampart.pos.y !== request.y
  ) {
    return { ready: false, status: 'rampart-identity-mismatch' };
  }

  if (rampart.my !== true) {
    return { ready: false, status: 'rampart-not-owned' };
  }

  if (rampart.isPublic === request.public) {
    return { ready: false, status: 'state-already-observed' };
  }

  return { ready: true, status: 'access-change-ready' };
}

A matching state is an observation, not evidence that this request caused it. Close or archive that request separately instead of manufacturing an accepted operation record.

Reserve the Rampart for the tick

function createRampartAccessDispatcher() {
  const reservedRampartIds = new Set();

  return {
    reserve(rampartId) {
      if (reservedRampartIds.has(rampartId)) {
        return {
          ready: false,
          status: 'rampart-already-reserved'
        };
      }

      reservedRampartIds.add(rampartId);
      return {
        ready: true,
        status: 'rampart-reserved'
      };
    },
    release(rampartId) {
      reservedRampartIds.delete(rampartId);
    }
  };
}

Every module that can change Rampart access must use the same dispatcher. A local reservation inside one role file cannot stop another independent module from overwriting the intent later in the tick.

Record only an accepted change

function submitRampartAccess(
  dispatcher,
  requestId
) {
  const request =
    Memory.rampartAccessRequests?.[requestId];

  if (!request || request.requestId !== requestId) {
    return { status: 'request-identity-mismatch' };
  }

  const rampart = typeof request.rampartId === 'string'
    ? Game.getObjectById(request.rampartId)
    : null;

  const decision = evaluateRampartAccess(
    request,
    rampart
  );
  if (!decision.ready) {
    return decision;
  }

  const reservation = dispatcher.reserve(rampart.id);
  if (!reservation.ready) {
    return reservation;
  }

  Memory.pendingRampartAccess ??= {};
  if (Memory.pendingRampartAccess[rampart.id]) {
    dispatcher.release(rampart.id);
    return { status: 'pending-operation-exists' };
  }

  request.enabled = false;
  request.lastAttemptAt = Game.time;

  const before = {
    isPublic: rampart.isPublic,
    roomName: rampart.pos.roomName,
    x: rampart.pos.x,
    y: rampart.pos.y
  };

  const result = rampart.setPublic(request.public);
  request.lastResult = result;

  if (result !== OK) {
    dispatcher.release(rampart.id);
    request.status = 'access-change-rejected';
    return {
      status: request.status,
      result
    };
  }

  Memory.pendingRampartAccess[rampart.id] = {
    submittedAt: Game.time,
    requestId: request.requestId,
    rampartId: rampart.id,
    roomName: rampart.pos.roomName,
    x: rampart.pos.x,
    y: rampart.pos.y,
    requestedPublic: request.public,
    before
  };

  request.status = 'accepted-awaiting-verification';

  return {
    status: request.status,
    result,
    rampartId: rampart.id
  };
}

The request is disabled before the mutation call, but the pending record is created only after OK. A rejection stays disabled for review; do not silently re-enable an access-control operation on the next tick.

Verify the exact object next tick

function verifyRampartAccess(pending) {
  if (!pending) {
    return { status: 'no-pending-operation' };
  }

  const expectedTick = pending.submittedAt + 1;
  if (Game.time < expectedTick) {
    return { status: 'waiting-for-next-tick' };
  }
  if (Game.time > expectedTick) {
    return { status: 'verification-window-missed' };
  }

  const rampart =
    Game.getObjectById(pending.rampartId);

  if (!rampart) {
    const room = Game.rooms[pending.roomName];
    const replacement = room
      ? room.lookForAt(
          LOOK_STRUCTURES,
          pending.x,
          pending.y
        ).find(structure =>
          structure.structureType
            === STRUCTURE_RAMPART
        ) || null
      : null;

    return replacement
      ? {
          status: 'original-rampart-missing-replacement-present',
          replacementId: replacement.id
        }
      : { status: 'original-rampart-unavailable' };
  }

  if (
    rampart.structureType !== STRUCTURE_RAMPART
    || rampart.pos.roomName !== pending.roomName
    || rampart.pos.x !== pending.x
    || rampart.pos.y !== pending.y
    || rampart.my !== true
  ) {
    return { status: 'rampart-identity-mismatch' };
  }

  return {
    status: rampart.isPublic
      === pending.requestedPublic
      ? 'rampart-access-state-observed'
      : 'accepted-state-not-observed',
    rampartId: rampart.id,
    beforePublic: pending.before.isPublic,
    requestedPublic: pending.requestedPublic,
    observedPublic: rampart.isPublic
  };
}

Identity is checked before state. A newly built Rampart at the same tile does not prove the original operation. A late observation may show the desired boolean, but it cannot prove which same-tick caller produced it.

Preserve missing and overwritten states

StatusMeaningResponse
rampart-already-reservedAnother request owns this Rampart for the tickDo not submit a second call
pending-operation-existsThe previous accepted call has not been verifiedVerify or archive it first
accepted-state-not-observedOK was returned but the target state is absent next tickInvestigate a later same-tick overwrite
verification-window-missedThe exact next-tick sample was not takenKeep the operation unverified
original-rampart-missing-replacement-presentThe saved ID vanished and another Rampart occupies the tileDo not transfer evidence to the replacement

Public is not an ally list

setPublic(true) allows other players' Creeps to pass. It does not accept usernames, alliances, body-part filters, schedules or intent rules. Those policies belong in a separate access dispatcher that decides whether to open or close the exact Rampart; the API still receives only one boolean.

Production integration boundary

Run one final Rampart-access dispatcher after all policy modules have proposed changes. Reject duplicate Rampart IDs, verify pending operations before accepting another request, and keep failed or missed samples visible. Console execution, live passage behavior, same-tick overwrite traces, genuine screenshots and CPU measurements remain pending.

Official documentation

Review the official Screeps API for StructureRampart.setPublic(), StructureRampart.isPublic, ownership, object IDs and return codes. The official engine implementation is also relevant when reasoning about same-tick intent replacement.

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.