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:
| Peripheral | Type | Role |
|---|---|---|
| Post Box | refurbished_furniture:post_box | SEND mail (packages its 6 inventory slots) |
| Mail Box | refurbished_furniture:mailbox | RECEIVE 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:packageitem, then callsDeliveryService.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:
sendMailreturns the vanillaDeliveryResultimmediately. The package sits on the mailbox’s delivery queue (capped by Refurbished’sdeliveryQueueSizeconfig) 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:
| Field | Meaning |
|---|---|
id | 1-based slot number - pass to readMail; shifts if a player rearranges the box |
name | display name of the stack |
count | stack size |
sender | package sender string, or nil |
message | package message string, or nil |
isPackage | true for refurbished_furniture:package items |
timestamp | always 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).
| Method | Meaning |
|---|---|
pullItems(fromName, [fromSlot], [limit], [toSlot]) → n | move items INTO the mail block |
pushItems(toName, [fromSlot], [limit], [toSlot]) → n | move items OUT of the mail block |
list() → table | contents, {[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 incom.petroid.ptr_utilities.compat.refurbished.*. - Block entity types and the
package_infodata 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 itsframeworkregistry helper classes. - The full CC:Tweaked jar is a
compileOnlydependency forServerContext/ServerComputeraccess (side-name resolution and computer ids); CC itself is a required mod at runtime.