Not translated yet — showing English.
Social
Social adds one concept: a relationship of two actors, with a state of its own and an initiator. A clan, a guild or a squad is a group with a relationship layer over it, not a second kind of thing; and blocking, which several modules need, lives here so that only one place owns it.
When to use it
- Players need each other by name: friends, followers, block lists.
- A clan or guild needs entry rules: an invitation from the group, a join request from an actor, and a decision on either.
- A friend list has to show who is online: presence is derived from sessions, and who may see it is a predicate you declare.
- Another module needs to know somebody is blocked: it reads that state from here rather than keeping its own.
- Skip it when the thing is a set of actors rather than a pair with a state: that is a group, and a group per pair would mean millions of two-person groups, each with its own lifecycle and entry rules.
Who does what
| Actor | Can | Cannot |
|---|---|---|
player | propose a relationship or follow; accept, decline or withdraw; break a mutual one; block and unblock; read their own relationships and the presence of related actors; subscribe to changes; submit a join request | read anyone else's list of relationships, under any participant relationship whatsoever |
moderator | decide on invitations and join requests where they hold the membership-administration atom | decide on an intent they hold no permission for — that answers forbidden |
The model
What a relationship declaration carries.
| Declares | What it is |
|---|---|
kind | symmetric — the pair needs both sides to agree, and the state machine below is about it; or one-sided — following, whose only state is active. Uniqueness per pair and the idempotency of a proposal hold for both |
re-invitation rule | after a refusal: forbidden · permitted after a declared period · permitted at once. Declared, because "ask again" is a product decision |
presence visibility | a predicate — to everyone · only to mutually connected · to nobody. There is no default |
joining mode (on the group type) | open · by request with a decision · by invitation only |
retention of declined and broken | after the declared period the relationship is removed, and re-inviting becomes possible again regardless of the re-invitation rule |
The states of a symmetric relationship.
| State | Meaning |
|---|---|
proposed | the initiator proposed and the other side has not answered |
mutual | both sides agree |
declined | the other side refused. The relationship is kept, because the re-invitation rule needs to know |
broken | one side left a mutual relationship |
blocked | one side blocked the other |
What is true of every relationship.
| Always | What it is |
|---|---|
one entity per pair | not two mirrored records. "A proposed to B" and "B was proposed to by A" are one fact read from two sides |
an initiator | is declared: who proposed, which display and the re-invitation rule both need |
blocked dominates | from it there is no transition to proposed or mutual |
a block | is asymmetric in control, symmetric in effect: only the one who set it may lift it, and it acts both ways |
the block state | is owned here and consumed elsewhere: messaging and others read it; none of them mutates it, and none keeps a copy |
presence | is derived from sessions: not written by anybody, and the visibility predicate is applied per requester rather than once per actor |
a deferred intent | does not occupy a seat: an invitation or a join request never counts towards the group's capacity, so a hundred requests can't exhaust a clan of fifty |
a group | keeps an administrator: at least one actor must hold the membership-administration atom, and the last one cannot simply leave: a clan whose last administrator walked out could never admit anyone again |
no intra-group roles | "clan officer" is an actor holding an atom, not a rank stored in a list |
an import never overwrites | relationships brought in from a login provider are additive: someone blocked does not become a friend because a provider says so |
Errors
- Already mutual is a conflict; there is nothing to propose.
- A proposal to oneself is a validation refusal.
- One of the sides has blocked answers not found — not forbidden, because a refusal that distinguished them would reveal the block. Repeating is pointless.
- A re-invitation before the deadline is a conflict, worth repeating after it.
- A limit exhausted — relationships, intents — is a conflict, not forbidden: the permission is held, but there is no capacity left. Retry once one frees up, or once the existing intents have been decided.
- An expired intent is a conflict: create a new one rather than retrying the old.
- The departure of a group's last administrator is a conflict until the permission has been handed on.
- Deciding on someone else's intent without the permission answers forbidden, and repeating is pointless.
- An import from a provider that is not connected answers unavailable: retry with backoff.
Limits
The numbers arrive with the platform limits chapter; what happens at each ceiling is fixed now.
- Mutual relationships per actor: a proposal is refused as a conflict; existing ones are never broken to make room.
- One-sided relationships per actor: a new one is refused; existing ones stay.
- Outgoing proposals: a new one is refused, and there is no eviction: an evicted invitation would look like a declined one.
- Blocks per actor: adding one is refused as a conflict, and older blocks are not evicted, so nobody is silently unblocked.
- An intent's lifetime:
expired, with an event. - The rate of proposals per actor: a rate limit with a deadline.
- The rate of presence changes in the stream: bounded by the update rate rather than by discarding changes.
- Retention of declined and broken relationships: removal by the declared period.