ptr_utilities

Refurbished Furniture Mail Peripherals

CC:Tweaked peripherals for the Refurbished Furniture mail system. Wire a CC:Tweaked wired modem directly onto a Post Box or a Mail Box and wrap it from a computer:

local postbox = peripheral.find("refurbished_furniture:post_box")
local mailbox = peripheral.find("refurbished_furniture:mailbox")

The two blocks have strictly separated roles, mirroring gameplay:

PeripheralTypeRole
Post Boxrefurbished_furniture:post_boxSEND mail (packages its 6 inventory slots)
Mail Boxrefurbished_furniture:mailboxRECEIVE mail (read the delivered stacks)

Requires the refurbished_furniture mod on the server. Disable with compat.enableRefurbishedMailPeripheral = false in config/ptr_utilities-common.toml (restart required).

How Refurbished mail actually works

Verified by decompiling refurbished_furniture-neoforge-1.21.1-1.0.22 (DeliveryService, Mailbox, PackageInfo, PackageItem, ServerPlayHandler.handleMessageSendPackage):

  • Sending from a Post Box wraps whatever is in the post box’s 6 slots into a single refurbished_furniture:package item, then calls DeliveryService.sendMail(mailboxId, packageStack).
  • The recipient is fundamentally a mailbox UUID; player/custom names are resolved by this API. Each placed Mail Box registers a mailbox with an id, an owner and an optional custom name (assigned through the naming prompt at placement; Refurbished never re-prompts afterwards).
  • Sending is synchronous: sendMail returns the vanilla DeliveryResult immediately. The package sits on the mailbox’s delivery queue (capped by Refurbished’s deliveryQueueSize config) and is moved into the mailbox inventory as the delivery service ticks (Mailbox.tick → deliverItem).
  • There is no subject and no timestamp in Refurbished’s data model. A package carries only a sender string and a message string (max 1024 chars).
  • A Mail Box is just an item container; there are no per-mail ids or read flags. Mail arrives as a physical package the player opens by hand, so this API has deliberately no unread/read tracking either.

Post Box (send)

sendMail([recipient], [message]){success, message, deliveredTo}

Packages the current contents of the post box and queues delivery. The box must be non-empty (fill it by hand, with a hopper, or via pullItems).

recipient, when given, is one of:

  • a mailbox UUID (from listMailboxes() / findMailbox() / getMailboxId()),
  • a custom mailbox name (wins over owner names),
  • an owner player name (only if it unambiguously maps to one mailbox; otherwise a Lua error tells you to pass the id).

When omitted, the post box routes to the single mailbox reachable from the calling computer (wired-network attachments plus computer sides) - errors if none or more than one is visible. This makes fixed backend setups trivial: post box + mailbox on one network, sendMail() “just works”.

message is optional (max 1024 chars). The sender is stamped as computer #<id> from the calling computer. On success the post box is emptied, exactly like the vanilla screen. Fails with a Lua error for empty boxes or banned items (Refurbished’s bannedItems / banSendingItemsWithInventories config applies), and returns success=false with the vanilla reason (“Unknown or invalid mailbox”, “The selected mailbox has reached its max mail queue”, “The selected mailbox is in an undeliverable dimension”). deliveredTo describes the routed mailbox (id, name, dimension, position) so every send is auditable.

local box = peripheral.find("refurbished_furniture:post_box")
local res = box.sendMail("Alice", "here is your diamond")
print(res.success, res.deliveredTo)

listMailboxes() → table

All registered mailboxes: id, owner, ownerId, customName (nil while unnamed), x, y, z, dimension.

findMailbox(name) → table

Same shape, filtered by exact owner or custom name (case-insensitive).

Mail Box (receive)

getMailboxId() → string

This mailbox’s delivery-service id - the unambiguous recipient id for sendMail on any post box on the network.

getMailboxOwner() → table

{hasOwner, owner, ownerId, customName, [error]}. owner/ownerId are nil until a player claims the mailbox; customName is nil until named.

getMailboxCoordinates() → table

{x, y, z, dimension} of the mailbox block.

listMail() → table

Every stack in the mailbox:

FieldMeaning
id1-based slot number - pass to readMail; shifts if a player rearranges the box
namedisplay name of the stack
countstack size
senderpackage sender string, or nil
messagepackage message string, or nil
isPackagetrue for refurbished_furniture:package items
timestampalways nil (Refurbished stores no time data)

readMail(id) → table

Full metadata for one mail plus, for packages, contents (the sealed item stacks with name/count) and totalItems. Ids are 1-based slot numbers, consistent with the inventory methods below. There is deliberately no unread/read tracking: mail is a physical package the player opens by hand, so a peripheral-side read flag would track nothing meaningful.

local mail = peripheral.find("refurbished_furniture:mailbox")
for _, header in ipairs(mail.listMail()) do
    local m = mail.readMail(header.id)
    print(("from %s: %s"):format(m.sender or "?", m.message or m.name))
end

Inventory transfer (backend flows)

Custom peripherals shadow CC:Tweaked’s generic inventory provider, so both mail peripherals additionally expose CC-style inventory methods. Any inventory peripheral on the same wired network (chest, barrel, modded machine) works as the remote side - resolved via NeoForge’s Capabilities.ItemHandler.BLOCK (plain Container fallback). Slots are 1-based, matching CC’s inventory API; 0 means “any slot”.

Peripheral name resolution covers both wired-network attachment names (e.g. refurbished_furniture:post_box_0) and computer-side names (left, top, …) - a modem-attached peripheral’s IComputerAccess only exposes the former, so side names are resolved through the calling computer’s ServerComputer.getPeripheral(ComputerSide).

MethodMeaning
pullItems(fromName, [fromSlot], [limit], [toSlot]) → nmove items INTO the mail block
pushItems(toName, [fromSlot], [limit], [toSlot]) → nmove items OUT of the mail block
list() → tablecontents, {[slot]={name,count}, ...}
getItemDetail(slot) → table{name, count, displayName}
-- amazon-ish backend: restock the post box from a warehouse chest, ship, collect
local box   = peripheral.find("refurbished_furniture:post_box")
local chest = peripheral.find("inventory")
box.pullItems(chest)                       -- load the box
box.sendMail(nil, "order fulfilled")       -- ship to the connected mailbox

local mail = peripheral.find("refurbished_furniture:mailbox")
mail.pushItems(chest)                      -- empty returns into storage

Implementation notes

  • Registration is gated on ModList.get().isLoaded("refurbished_furniture") plus the config toggle; all Refurbished imports live in com.petroid.ptr_utilities.compat.refurbished.*.
  • Block entity types and the package_info data component are resolved through the vanilla registries (refurbished_furniture:post_box, refurbished_furniture:mail_box, refurbished_furniture:package_info) because the Refurbished jar does not bundle its framework registry helper classes.
  • The full CC:Tweaked jar is a compileOnly dependency for ServerContext/ServerComputer access (side-name resolution and computer ids); CC itself is a required mod at runtime.