Brodgar client

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