vetra-example-adapter ships in the same archive as the product. It is optional: install it
only if you are writing an adapter and want a working one to read.
Read this rather than a built-in adapter. The props adapter is several hundred lines of Source
engine detail and almost none of it is the contract. Everything here is.
The domain it integrates
A fictional addon that stores named markers on a map: a navigation waypoint, a shop location, a spawn zone label. That domain was chosen for three properties most real integrations share, and that props and NPCs happen not to have:- a marker is not an entity. There is nothing in the world to find;
- a marker has no model, so it cannot be previewed as one;
- a marker has its own durable record id, which is what makes honest persistent identity possible.
1. The addon being integrated
Everything in this section is the “third-party addon”. None of it knows what Blueprints is, which is the point: an adapter is a bridge, never a fork.weak and this adapter would have no business taking part in a restore.
2. Registration
example.markers
vendor.domain. Two authors both shipping markers is a collision; example.markers and
someoneelse.markers is not.display only
Renaming it does not change the adapter’s identity. Ids live inside every Version ever
taken; display names do not.
implementation
Bump for any change.
record shape
A different number entirely, and it moves only when an older record can no longer be read
correctly.
persistent
Honest here, because the counter above is persisted and never reused.
lua/autorun/server/. This addon may load
before or after Blueprints, and adding a handler works in both directions.
3. The dependency
4. Capture
ForMap sorts by id, so two consecutive captures name
the same objects in the same order.
source = "adapter" because the id came from this addon’s own durable store; persistent
because that store outlives a restart.
The coordinates are plain numbers. Never a Vector or an Angle: a Version is written to
disk as JSON, and an engine handle in it makes the file unwritable and the record a lie.
5. Transform
false plus a non-empty reason is the contract.
6. Materialize
orientation = "yaw". A marker is a point with a facing, and pitch or roll would be a
promise this adapter cannot keep. Declaring it here is what stops a post-restore check
reporting a perfectly placed marker as mis-rotated on two axes nothing ever wrote.
preview = { kind = "point" }. No model, and that is a first-class answer rather than an
omission. An adapter with geometry would return { kind = "bounds", mins = ..., maxs = ... }
instead.
7. Properties
Collect captures, and nothing else.
Note that orientation = "yaw" appears again, in PrepareProperties. That is not a copy
and paste mistake: because this adapter declares properties, post-restore verification asks
this function, and an adapter that declared yaw in one and nothing in the other would be
verified as "full".
The read-back is the capability contract made real rather than promised.
8. Remove
9. Trying it
The addon ships a console command so the example can actually be exercised. As an admin, in the server console or in game:What to copy from it
- Registration from the hook, in
lua/autorun/server/. - A persisted counter for identity, and the honesty to declare
persistentonly because of it. - Refusing unknown ids with
falseplus a reason. - Declaring
orientationfrom both prepare functions. - Reading values back after writing them.
- A dependency check that tests readiness, not existence.