hafen.map: Segments and Grids
The database is made of segments, each made of grids. A segment is one contiguous explored area, what the map window draws as one map. A grid is a 100×100-tile square the server hands out. Read the ground the client recorded, and ask what it says about a place.
local session = hafen.session():current() -- the character on screen
local position = session:player():gob():position()
local durable = position:info() -- where it stands, in the durable form
local grid = hafen.map():grid():get(durable.gridId)
local cell = { x = math.floor(durable.x / 11), y = math.floor(durable.y / 11) }
hafen.log():write(grid:tile(cell).name .. " == " .. session:world():tile(position).name) -- the same tileset
| Method |
Returns |
Permission |
Description |
hafen.map():segment():current() |
Segment \| nil |
Unprotected |
The segment the character on screen stands in. nil until the map has streamed in. |
hafen.map():segment():get(id) |
Segment \| nil |
Unprotected |
One segment by id. nil if the database has none. |
hafen.map():segment():list() |
Segment[] |
Unprotected |
Every segment the database holds, in id order. |
hafen.map():grid():get(grid_id) |
Grid \| nil |
Unprotected |
One grid by the server’s id. nil if never recorded. |
| Rule |
Detail |
hafen.map():grid() does not enumerate |
It says so rather than answering an empty list: the database holds every grid you have walked over. Address one by id, or walk a rectangle of one segment. |
| An id is a decimal string |
A number is refused: hafen.map():segment():get(1234) raises. Pass segment:id(), grid:id() or the gridId out of a stored position. |
| One Grid, two doors |
The live world publishes the server’s grid id and the database keys on the same number, so session:world():grid():at(position) and hafen.map():grid():get(id) hand back the same object. Each door answers nil for what its half lacks. grid:tile(cell) and session:world():tile(position) agree where both answer. |
| Each half on its own |
Ground under your feet not yet saved is :live() true and :exists() false with every recorded read nil. Ground explored long ago is the reverse. |
The Segment object
| Method |
Returns |
Permission |
Description |
segment:id() |
string |
Unprotected |
The segment id, a 64-bit value as a decimal string: the identity. |
segment:exists() |
boolean |
Unprotected |
Whether the database still carries it (a merge can fold one into another). |
segment:grid():get(coordinate) |
Grid \| nil |
Unprotected |
The grid at segment grid coordinate {x, y}. nil for no grid there and for one still loading. |
segment:grid():list(area) |
Grid[] |
Unprotected |
Every loaded grid in {x, y, w, h} of segment grid coordinates. At most 1024 coordinates per call, a bigger rectangle refused. |
segment:markers(filter) |
Marker[] |
Unprotected |
The markers recorded in this segment. |
segment:info() |
table |
Unprotected |
{ id, current, markers }. |
You ask a segment for an area, never for a list: segment:grid():count() refuses. The client’s own minimap walks the grid coordinates of the rectangle it draws, and so do you.
local session = hafen.session():current()
local segment = hafen.map():segment():current()
local current_grid = session:world():grid():at(session:player():gob():position())
local centre = current_grid:segmentCoord()
for _, grid in ipairs(segment:grid():list{ x = centre.x - 2, y = centre.y - 2, w = 5, h = 5 }) do
local corner = grid:position() -- a Position: durable, and locatable here
if corner:x() then hafen.log():write(grid:id() .. " at " .. corner:x() .. ", " .. corner:y()) end
end
The Grid object
| Method |
Returns |
Permission |
Description |
grid:id() |
string |
Unprotected |
The server’s grid id: the identity, what a stored position anchors on. |
grid:live() |
boolean |
Unprotected |
Whether it is streamed in right now. |
grid:exists() |
boolean |
Unprotected |
Whether the database carries it. |
grid:segmentCoord() |
{x, y} \| nil |
Unprotected |
Its coordinate inside its segment. |
grid:position() |
Position \| nil |
Unprotected |
Its upper-left corner. |
grid:segment() |
Segment \| nil |
Unprotected |
The segment it belongs to. |
grid:tile(cell) |
{name?, prio} \| nil |
Unprotected |
The recorded tile at within-grid tile coordinate {x, y}, 0..99. name absent where the recorded tileset carries none. Anything outside 0..99 is refused. |
grid:height(cell) |
number \| nil |
Unprotected |
The recorded height there. |
grid:modified() |
number \| nil |
Unprotected |
When the client last recorded this grid, in milliseconds. |
grid:mask() |
mask collection |
Unprotected |
Which claims and provinces covered it. |
grid:image(level) |
image \| nil |
Unprotected |
Its minimap drawing at zoom level (default 0). |
grid:overlayImage(tag) |
image \| nil |
Unprotected |
One recorded mask drawn in the overlay’s own colour. |
grid:info() |
table |
Unprotected |
{ id, seg, sc, pos?, live, mtime?, loaded, size }. size is the grid’s span in tiles, {w, h}. |
| Rule |
Detail |
grid:tile names the tileset |
A resource name, not a tile id. The live session:world():tile id is a number one session made up, so the name is what the two halves compare on. |
| What the client recorded |
Current for the ground under a character of yours (re-recorded as they move). A year old for ground explored a year ago. grid:modified() says how old. |
Storing a place
Store the Position, never the segment coordinate. A segment id is bookkeeping this client invented. When two explored areas turn out to touch, the merge rewrites the absorbed segment’s grid coordinates and every marker inside it. A stored seg + tile coordinate would then point at the wrong place. A Position anchors on a grid id from the server, meaning the same to every player, which no merge moves.
| Read it as |
From |
Then |
| A Position |
gob:position(), marker:position(), grid:position() |
Store this, send this. hafen.store and hafen.json marshal it. The {gridId, x, y} table position:info() hands out is for a shape leaving the client another way, and session:world():position(saved) brings it back. |
| Segment id + lattice coordinate |
segment:id(), marker:segmentTile(), grid:segmentCoord() |
Look at it, compare it this session, never store it. |
See Also
- The map database — the
nil-until-loaded rule these reads follow, and interning.
session:world — the live half, and the other door onto this same Grid.
- Drawings — what
grid:image hands back.
- Overlays — what
grid:mask() hands back.
- Coordinates — the coordinate spaces, and which one you may store.