Brodgar client

Permissions

Reading the game needs no permission. Acting on it (walking, clicking an object, using an item, picking a menu entry) is protected. There is one key per action, declared in your manifest and approved by the user when they enable your addon. This guide is the catalogue of keys, how to ask for them, and what needs no key.

{ "permissions": ["player.move", "gob.click", "item.*"] }

The catalogue

One key per protected action, named <section>.<verb> after the section the verb lives on (pagina:use() is menugrid.use, slot:use() is actionbar.use). Where one action has two doors both are behind one key. The third column is the consent dialog’s line, word for word.

Key Verb Lets an addon
player.move session:player():move walk your character to a place
player.hand.use session:player():hand():use use whatever it is holding on things
gob.click session:world():click click objects in the world
item.use item:use use items
item.take item:take pick items up onto the cursor
item.drop item:drop drop items
item.transfer item:transfer move items between containers
world.place session:world():place place buildings and objects
world.select session:world():select select an area of the ground
map.marker hafen.map():marker():add, :remove, marker:color, :onMap add and delete pins on your map, and recolour them
menugrid.use pagina:use invoke entries of the action menu, on any of your characters
flowermenu.select session:flowermenu():select choose from the radial menu of any of your characters
flowermenu.cancel session:flowermenu():cancel dismiss the radial menu of any of your characters
craft.make session:craft():make press the Craft button, on any of your characters
actionbar.use slot:use press the action-bar buttons of any of your characters
actionbar.res slot:res assign one of the game’s own actions to any of your characters’ action-bar buttons
actionbar.clear slot:clear empty any of your characters’ action-bar buttons
fight.use action:use use combat actions, in the fights of any of your characters
fight.set session:fight():opponent():set switch the target, in the fights of any of your characters
fight.pursue session:fight():pursue press Pursue beside an opponent’s portrait, in the fights of any of your characters
fight.give session:fight():give click the give button beside an opponent’s portrait, in the fights of any of your characters
fight.load school:load load a saved combat school, on any of your characters
fight.save school:save save the combat-school layout into a slot, on any of your characters
kin.add session:kin():add add someone to any of your characters’ kin lists
kin.rename kin:rename rename someone on any of your characters’ kin lists
kin.group kin:group change someone’s kin group, on any of your characters
kin.end kin:endKin end kinship with someone, on any of your characters
kin.forget kin:forget forget someone from any of your characters’ kin lists
chat.send channel:send say a line in the chat, as any of your characters
speed.set session:speed():set change the movement speed of any of your characters
session.close session:close, hafen.session():remove log out any of your characters
session.add hafen.session():add(user) log in any of the accounts you told the login screen to remember
session.forget hafen.session():forget(user) forget any of the accounts you told the login screen to remember, so it asks for their password again
widget.send widget:send, event:resend, event:send send any message the client itself could send
widget.value widget:value flip the client’s own controls — a box it ticks, a field it types into — which the server sees
ui.resend event:resend on a control re-run a button you pressed, so the client sends what that press sends
ui.focus session:chat():selected(channel) move the keyboard into a chat entry line, so what you type next goes there
virtual.click hafen.virtual():click click the controls it has standing in the world, which act as if you had clicked them
client.settings every option write, binding:key(key), a key button bound to a binding not yours change your client settings and hotkeys
console.run session:console():run run any of the client’s console commands, on any of your characters, including ones that run code outside the addon sandbox
http.get request:send on a GET fetch data from the servers it lists
http.post request:send on a POST send data to the servers it lists
websocket.connect connection:connect keep a live connection to the servers it lists
voice.connect voice:connect use your microphone to talk on the voice servers it lists
Rule Detail
The whole set Nothing else is protected, and no key grants the tier whole: an addon that declared gob.click can click objects and nothing else on the list.
console.run is the widest It names a surface, the command line, so it covers every command the client dispatches. :lua is one, and it evaluates outside the sandbox. An addon holding this key can do what the user’s console can.
Keyed without reaching the game server map.marker deletes a pin no server restores. client.settings rewrites every hotkey and reaches the client’s own bindings (binding:key("Ctrl+I") on inv). http.*, websocket.connect and voice.connect reach outside the client, the last opening the microphone. Each is a control the player has in front of them.

A key names the action, not the target

The client holds several logins, and a protected verb is addressed at one (session:kin():add adds a kin to the character session names, drawn or not). The key you declared covers every one of them. A verb is protected because it starts something the player could have performed, and the player could have tabbed to that character. Read every line of the table across the whole client. What the user approves is a capability. The character it is pointed at is your addon’s to choose. gob.click is the one key not named for the section its verb sits under. It names the action, clicking an object. The verb, session:world():click, sits on the world of the character clicking. A library’s exported function runs under the library’s own keys, whoever called it; a callback you hand a library runs under yours (addons).

Groups

A <prefix>.* entry stands for every key under that prefix.

Group Covers
item.* item.use, item.take, item.drop, item.transfer
kin.* kin.add, kin.rename, kin.group, kin.end, kin.forget
world.* world.place, world.select
flowermenu.* flowermenu.select, flowermenu.cancel
actionbar.* actionbar.use, actionbar.res, actionbar.clear
fight.* fight.use, fight.set, fight.pursue, fight.give, fight.load, fight.save
player.* player.move, player.hand.use
player.hand.* player.hand.use
widget.* widget.send, widget.value
ui.* ui.resend, ui.focus
http.* http.get, http.post
session.* session.add, session.close, session.forget
Rule Detail
Any prefix is a group gob.*, menugrid.*, craft.*, speed.*, map.*, client.*, console.*, chat.*, virtual.*, websocket.* and voice.* parse, each a longer spelling of its single key.
Whole dot segments A group never reaches a key that merely starts with the same letters, and does reach a nested one. player.hand.use is the only nested key, covered by player.* and alone by player.hand.*.
The unit the user reads The dialog prints one line per entry as written (item.* is one line naming its verbs), and the panel row counts entries the same way.
No "*" An entry that is neither a key nor a group (a typo, an invented key, a bare asterisk) is a load error. The addon does not run. The panel row names the entry and lists the vocabulary.
Tolerated "" or a string of spaces is dropped. Entries are read trimmed (" item.take " is that key). A key declared twice is one grant, printed once.

Declaring it

Step Detail
1. The manifest declares a key or a group for every protected verb you call "permissions": ["player.move", "gob.click", "item.*"].
2. The user enables the addon An addon that declares anything here is disabled the first time the client sees it, unless the client installed it from its own release, which grants what that copy declares (addons the client ships). Enabling it in the AddOns manager raises a consent dialog listing exactly the entries you wrote, one line each. The tooltip of its name lists the entries, and Enable all skips it.
Rule Detail
Asking for more re-asks The approved keys and the hosts shown beside them are remembered per addon. A version adding either is disabled and prompts again with the addition marked new. A widened group counts as added, and so does a host no approved entry covers (b.example.com re-asks, narrowing *.example.com to a.example.com does not). Dropping a key or a host never re-prompts.
An undeclared key A protected verb called by an addon that did not declare its key raises naming the verb, the key and the manifest line to paste.
The gate asks the record, not the manifest What the user approved for your addon: a key added to manifest.json after consent is refused at the call until the addon is enabled again. Both halves must hold: a key no longer declared is no longer granted.
The gate asks who is running A protected verb reads the manifest of the addon whose code is on the stack. Handing another addon an event, a Widget or a Session hands it nothing.
The record has a fixed size A declaration large enough to overflow it (a network block of hundreds of hosts) cannot be recorded. Approving it is refused naming your addon and the limit, and the addon stays disabled until the declaration is smaller.

What is not protected

Everything else writes to your own client and needs no key.

Unprotected write Changes
session:menugrid():add An entry of your own in a character’s action menu.
slot:hold(pagina) Which of your entries the client draws over a bar slot. The server’s content stays and comes back untouched.
category:show(flag) Which icons your minimap draws.
session:chat():selected(channel) Which tab a character’s chat has on screen. The keyboard moves with it only for ui.focus.
widget:position, :size, :visible, :draggable, :resizable, :remember Where the client’s own windows sit and how big they are, whether the user can drag or size one, whether that lasts.
widget:replace(view) Which window a client toggle opens.
hafen.ui():sheet() What the client looks like.
hafen.virtual Props only you can see. Every verb but :click, which is virtual.click.
hafen.sound What you hear.
Option reads Nothing. Every client option write is client.settings.
option:value(value), :add(), options:panel(fn) Your own options and page.
Rule Detail
The line Whether the user would have to undo it by hand. Everything above is a display choice changed back in a click, or something only your addon sees. A deleted pin, a rebound hotkey, a request to another host and a connection kept to one are not.
A menu entry of yours Naming it, drawing it, putting it on the bar and running your function on a click never reach past your client. pagina:use() keeps menugrid.use whichever entry it names.
Re-issuing an action needs widget.send event:resend() and event:send(table) put a message on the wire widget:send uses, with arbitrary arguments, as often as a handler loops. event:preventDefault() stays open: cancelling reaches nothing.
Re-running a control’s action needs ui.resend event:resend() on a client control runs its method, its own wdgmsg. Narrower than widget.send: it re-runs the press the user made, once per event. A second event:resend() on the same event raises.
Two keys, one act kin.end and kin.forget are one message on the wire, resolved by the entry’s state. An addon granted only kin.end can endKin() an un-kinned entry and forget it. One granted only kin.forget can end a kinship. Granting either grants the pair.

Writing an addon that acts

hafen.console():on("gotree", function()
  local session = hafen.session():current()           -- the character on screen
  local tree = session:world():gob():nearest("terobjs/tree")
  if tree then
    session:player():move(tree:position())            -- player.move
    session:world():click(tree, 3)                    -- gob.click, and open its radial menu
  end
end)
Habit Detail
Your manifest answers “may I act” A declared, running addon was granted the key: nothing to test at run time. A file shipping either way reads its own manifest.json rather than provoking the error.
Act from SessionEnteredWorld onwards Every verb here needs a live map view or object and throws before one exists. A verb at a character not yet in the world says so and sends nothing.
Make the user ask Bind actions to a hotkey, a command or a setting, not a timer. An addon acting on its own at load is what a dialog cannot warn about.
Declare the narrowest set The dialog is the user’s whole view of what you do. A group asked for out of convenience reads as every capability in it.

What the permission does not buy

Rule Detail
The server is the authority An addon sends what a click could have sent and finds out what happened by watching the world. A refused action is refused server-side with nothing to catch.
Some of it outlives the addon slot:res(name) and slot:clear() write the character’s bar on the server, and school:save() the school saved in that slot: disabling, :reload and logout put nothing back. The user undoes it in the game. slot:hold(pagina) is unprotected beside them because the server’s content comes back untouched.

Network: one key, and the hosts are its argument

http.get, http.post, websocket.connect and voice.connect are keys like any other. The network block is what they take. The key says whether, the hosts say where.

{ "permissions": ["http.get", "websocket.connect"],
  "network": { "hosts": ["api.example.com"] } }
Rule Detail
One line per key over the same hosts “fetch data from the servers it lists: api.example.com”. “keep a live connection to the servers it lists: api.example.com”. For a voice link, “use your microphone to talk on the voice servers it lists”.
Load error, refusal Hosts without a key is a load error naming the keys. A key with no hosts is refused at the call. An origin the user did not approve is refused before any request leaves, connection opens or microphone is taken.
An entry One scheme on one port. A wss:// address is the https server the block names. A wildcard covers one domain’s sub-domains, never a top-level domain (what an entry means).
The panel The exact hosts in the tooltip of the addon’s name.

Next: theming — changing what the client looks like, which needs no permission.