# Groups and raids (social.party) ## What it is for `social.party` keeps the membership of player groups for a game: who leads, who is a tank, a healer or a damage dealer, who is online, which subgroup of a raid each member sits in, and which invitations are still open. One coordinator owns the membership of every group in its world, so an actor can never be in two parties at once. The kit holds rules and state only: it draws nothing, and it has no clock of its own (the game drives time with `step`). ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "social.party" } gripforge_gamekit_configure { project_id: "gkp_…", id: "social.party", config: { "maxParties": 64, "inviteFrames": 3600 } } ``` It requires `game.engine` and provides the exclusive service `social.party`. A coordinator called `default` exists as soon as the kit starts, built from the configuration above. ## First example ```text social.party.request("default", { "op": "create", "party": "p", "actor": "ann", "role": "tank" }) social.party.request("default", { "op": "invite", "party": "p", "actor": "ann", "target": "bob" }) social.party.request("default", { "op": "accept", "party": "p", "actor": "bob", "role": "damage" }) social.party.groupFor("bob") // { "party": "p", "members": { "ann": {...}, "bob": {...} } } social.party.groupFor("zed") // null: not grouped ``` Creating a second party for someone already grouped is refused. ## Settings | Setting | Type | Default | Meaning | |---|---|---|---| | `maxParties` | integer 1 to 256 | `64` | groups one coordinator can hold | | `inviteFrames` | integer 1 to 36000 | `3600` | lifetime of an invitation, in fixed simulation frames | Unknown keys and out-of-range values are reported when the configuration is validated. ## Services - `social.party` (service), methods: - `create(id, config)`: makes another coordinator named `id` (at most 128). `config` may name a `preset` of the social_party_presets collection; the other keys of `config` override it. - `request(id, command)`: runs a command on that coordinator and returns `{ state, events }`. An unknown coordinator is an error. - `step(id?)`: advances one fixed frame (default `default`) and returns `null`; invitations that reach their end vanish. - `groupFor(actor)`: `{ party, members }` of that actor in the `default` coordinator, or `null`. - `ids()`: the coordinator names. `remove(id)`: deletes one and returns whether it existed. ### Commands of `request` Every command is an object with an `op`; unknown fields are refused. Group commands carry `party` and the acting `actor`. | `op` | Fields | Rule | |---|---|---| | `create` | `role` | makes the party, the actor leads; refused if the actor is grouped, the party exists or `maxParties` is reached | | `invite` | `target` | leader only; the target must be ungrouped; at most 40 open invitations; capacity 5 (40 in a raid) | | `accept` | `role` | the invited actor joins, taking the first subgroup with room | | `leave` | none | the first remaining member in alphabetical order leads if the leader leaves; an empty party disappears | | `kick` | `target` | leader only; the leader cannot be kicked | | `promote` | `target` | leader hands over leadership to a member | | `role` | `target`, `role` | the leader, or the member for themselves; roles are `tank`, `healer`, `damage` | | `subgroup` | `target`, `subgroup` | leader only; 0 to 7 in a raid, 0 otherwise; at most 5 per subgroup | | `online` | `online` | a member marks themselves online or offline | | `configure` | `raid`, `loot` | leader only; `loot` is `personal` or `round_robin`; leaving raid mode needs at most 5 members, all in subgroup 0 | Read-only commands: `status` (the whole state), `config`, `save`, `preview` (runs a command on a copy and discards it), `load`. ## Events Each decision that is not a read emits an event; the engine ticks alone expire nothing. ```text social.party.created { instance, preset } social.party.removed { instance } social.party.changed { instance, op, result } social.party.refused { instance, op, by, reason } ``` ## Hooks and modding Two hooks let a game or a mod decide before the kit does: - `social.party.create`: asked before a coordinator is made; the payload has `instance`, `preset` and `config` and the `config` can be changed. A cancelling tap makes `create` fail with "creation refused". - `social.party.request`: asked before every command that changes something; the payload has `instance`, `op` and `command`, and the `command` can be rewritten. A cancelling tap returns `{ ok: false, cancelled: true, by, reason }` and emits the refused event. Content can send a command with the verb social_party_request and test that a coordinator exists with the condition social_party_exists: ```json { "social_party_request": { "instance": "default", "command": { "op": "leave", "party": "p", "actor": "bob" } } } ``` ## Engines The rules are the same Rust module on the web, in the native player and in the engine wrappers; there is nothing to draw. ## When it does not do what you expect - An invitation never expires: nothing ticks it by itself. Call `step` once per fixed frame from the host that owns the group. - `accept` fails with "invitation absent or expired": more than `inviteFrames` steps passed, or the actor was never invited. - `invite` fails with "leader required": only the leader invites, kicks, promotes or configures. - `create` fails with "party limit": raise `maxParties` (at most 256). - `groupFor` only looks at the `default` coordinator; use `request` with `status` for others. - A load that does not match the kit configuration is refused and live state is kept. ## Limits - Roles are `tank`, `healer` and `damage`; a party holds 5 members, a raid 40 in 8 subgroups of 5. - No chat, matchmaking or loot rolling: `loot` only records the chosen mode for the game to apply. - Reconcile actors with your server when you remove a coordinator. ## Versions Kit version 1.0.0; it relies on `game.engine` 1.x. The party is part of the game save.