Skip to content

Stable UUIDs

Vicious Squid edited this page Sep 14, 2026 · 2 revisions

Fio gives every brush and placeable entity a stable UUID when it is created. The UUID is the object's persistent identity and is retained across saving, loading, editing, streaming and runtime state changes.

A name such as Light_3 is human-readable and can change. A UUID is the identifier that Fio uses when it needs to know that an object is still the same object.

Brushes

Brushes store their UUID directly in the brush dictionary:

brush['id']

When a brush is serialized, its existing id is preserved. If an older brush has no ID, Fio assigns one with uuid.uuid4() during serialization or loading.

Things

Placeable entities store their UUID in their properties:

thing.properties['id']

A Thing receives a UUID when it is constructed if one is not already present:

self.properties['id'] = str(uuid.uuid4())

When an entity is loaded from a map, its existing properties are passed back into the constructor, so an existing UUID is retained rather than regenerated.

UUIDs are not names

Fio deliberately separates identity from display names.

For example:

name = "Door_1"
id   = "550e8400-e29b-41d4-a716-446655440000"

Renaming the door does not change its UUID.

This is important for systems such as I/O connections and logic graphs, where an object needs to remain identifiable even when its human-readable properties change.

Save and load

Map format version 3 includes stable entity IDs.

During save, every brush is guaranteed to have an ID before being serialized:

brush.setdefault('id', str(uuid.uuid4()))

During load, missing IDs are backfilled for legacy maps. Existing IDs are left untouched.

The same principle applies to entities through Thing.to_dict() and Thing.from_dict().

The result is:

create
  ↓
UUID assigned
  ↓
save
  ↓
load
  ↓
same UUID

The UUID is therefore persistent map data, not a temporary runtime identifier.

I/O uses UUIDs

Fio's I/O system can identify an entity by its stable ID rather than relying exclusively on its name.

The editor provides an entity finder based on stable IDs, allowing an I/O connection to resolve its target by UUID. This prevents a connection from becoming ambiguous simply because an entity was renamed.

Logic-graph nodes likewise use the entity's stable UUID as their identity.

Undo and redo

Stable IDs also matter to editor state.

Undo/redo serializes the scene rather than treating an object's position in a Python list as its permanent identity. Runtime renderer and geometry caches are stripped from these snapshots, while persistent object data—including IDs—is retained.

This allows an object to survive editor state restoration without acquiring a new identity.

Big World

Big World relies heavily on stable UUIDs.

Its spatial cells are not the object's identity. A brush or entity can move between cells, be activated, deactivated or streamed without receiving a new UUID.

Big World therefore treats:

UUID = object identity
cell = spatial metadata

Cell membership is derived from the object's position and the shared 512-unit grid rather than being stored as permanent identity data.

This is particularly important for streaming: parking an object in an inactive cell does not replace or recreate it.

Savegames and runtime restoration

Fio's savegame system can restore state onto an already-running world by matching objects using their stable UUIDs.

The scene does not need to be rebuilt simply to restore saved properties. The existing objects remain the same objects; their mutable state is overlaid onto them.

This preserves runtime object identity, which is important for references, I/O connections and other systems that already hold references to the objects.

UUID stability across streaming

Big World explicitly verifies UUID stability across a streaming round-trip.

The persistence layer can collect the UUID sets before and after streaming:

collect_uuids(...)

and compare them with:

uuids_stable(before, after)

A successful result requires the brush UUID set and entity UUID set to remain identical.

Streaming therefore changes where an object is active, not which object it is.

Why stable UUIDs matter

Stable UUIDs give Fio a common identity mechanism across otherwise independent systems:

  • Editor — objects can survive save/load and undo/redo.
  • I/O — connections can refer to specific entities.
  • Logic graphs — nodes can be associated with their underlying entities.
  • Savegames — runtime state can be restored onto existing objects.
  • Big World — streaming does not change object identity.
  • Persistence — deltas can describe changes to specific objects.
  • Legacy maps — objects without IDs can be assigned one automatically.

The important invariant is simple:

An object's UUID belongs to the object, not to its name, position, cell or current runtime state.

As long as the object itself persists, its UUID persists with it.

Clone this wiki locally