ptr_utilities

Card Reader

A CC:Tweaked peripheral block that reads Create: Numismatics cards. When a player right-clicks the block with a card, it captures the card data and fires a card_scanned event to all attached computers.

Peripheral Type

ptr_utilities:card_reader

Setup

  1. Craft a Card Reader (shapeless: any Numismatics card + railway casing + bank terminal)
  2. Place it next to a CC:Tweaked computer
  3. Wrap it: local reader = peripheral.find("ptr_utilities:card_reader")

The player who places a Card Reader becomes its owner; only the owner (or creative/admin players) can break it or wrench it off.

How Card Scanning Works

A player walks up to the block and right-clicks it while holding a Numismatics card. The block captures the card data in memory (not persisted to disk), creates a short-lived banking session, and fires a card_scanned event.

Card data is lost on chunk unload or server restart.

Events

card_scanned

Fired when a player scans a card at the reader.

local event, data = os.pullEvent("card_scanned")
-- data is a table with:
-- data.type              = "bank" | "authorized" | "id"
-- data.accountID         = UUID string (for bank/authorized cards)
-- data.authorizationID   = UUID string (for authorized cards only)
-- data.playerID          = UUID string (for ID cards only)
-- data.scannedBy         = UUID string of the player who scanned
-- data.scannedByName    = name of the player who scanned
-- data.sessionID         = UUID string of the banking session
-- data.sessionExpiresAt  = unix timestamp when session expires
-- data.terminalID        = UUID string of this card reader terminal

Methods

Card & Session Data

MethodReturnsDescription
getCardData()tableReturns all current card data + session info. Keys: type, accountID, authorizationID, playerID, scannedBy, scannedByName, timestamp, terminalID, sessionID, sessionExpiresAt, sessionConsumed.
getAccountID()string|nilThe scanned card’s account UUID.
getAuthorizationID()string|nilThe scanned authorized card’s sub-account UUID.
getSessionID()string|nilThe current banking session UUID.
getTerminalID()stringThis reader’s unique terminal UUID (persisted across restarts).
clear()voidClears all scanned card data and the current session.

Bank Queries (read-only, no card required)

These query the global Numismatics bank directly. No card scan needed.

MethodReturnsDescription
getAccounts()string[]All bank account UUIDs.
getBalance(accountID)intBalance in spurs.
getAccountLabel(accountID)string|nilDisplay name of the account.
accountExists(accountID)booleanWhether the account exists.
isPlayerOwned(accountID)booleantrue if player account, false if blaze banker.
isAuthorized(accountID, playerUUID)booleanWhether the player is authorized to use the account.
getTrustList(accountID)string[]|nilPlayer UUIDs on the account’s trust list (blaze bankers only).
getAccountInfo(accountID)tableDetailed info: id, isPlayerOwned, balance, displayName, trustList, trustListSize.
getAuthorizedAccounts(playerUUID)string[]All accounts a player is authorized to use.

Transfer (requires card scan)

MethodReturnsDescription
transfer(toAccountID, amount)tableTransfers funds from the scanned account to toAccountID. Requires an active session (card scanned). Session is consumed after successful transfer.

Routing depends on the scanned card:

  • Plain bank card / ID card → direct deduct/deposit through this mod, only when security.allowUnrestrictedTransfers=true in config/ptr_utilities-server.toml (default false, which rejects with “Transfers require an Authorized Card”).
  • Authorized card → Numismatics sub-account flow (authorization type + spend limit enforced by Numismatics).

Transfer receipt format:

{
    transactionID = "uuid",
    operation     = "transfer",
    account       = "from-account-uuid",
    source        = "from-account-uuid",
    destination   = "to-account-uuid",
    amount        = 640,
    timestamp     = 1234567890,
    mac           = "hmac-string"
}

Sessions

When a card is scanned, a session is created:

  • Tied to the terminal (not the computer)
  • Expires after 60 seconds
  • Consumed after a successful transfer
  • Cannot be reused once consumed

Check session status via getCardData() — the sessionConsumed and sessionExpiresAt fields tell you if the session is still usable.

Example: Wait for card, then transfer

local reader = peripheral.find("ptr_utilities:card_reader")

print("Waiting for card...")
local event, data = os.pullEvent("card_scanned")

print("Card scanned by " .. data.scannedByName)
print("Account: " .. (data.accountID or "none"))

if data.type == "bank" or data.type == "authorized" then
    local balance = reader.getBalance(data.accountID)
    print("Balance: " .. balance .. " spurs")

    -- Transfer 100 spurs to another account
    local receipt = reader.transfer("destination-account-uuid", 100)
    print("Transfer OK: " .. textutils.serialize(receipt))
end

Coin Denominations

CoinSpurs
Spur1
Bevel8
Sprocket16
Cog64
Crown512
Sun4096