onelobby 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +2 -2
  2. package/blueprint.schema.json +244 -0
  3. package/dist/blueprint.d.ts +170 -0
  4. package/dist/blueprint.js +286 -0
  5. package/dist/blueprint.js.map +1 -0
  6. package/dist/codec.d.ts +23 -0
  7. package/dist/codec.js +28 -0
  8. package/dist/codec.js.map +1 -1
  9. package/dist/direct/browser-host.d.ts +46 -0
  10. package/dist/direct/browser-host.js +93 -0
  11. package/dist/direct/browser-host.js.map +1 -0
  12. package/dist/direct/hub-host.d.ts +34 -23
  13. package/dist/direct/hub-host.js +75 -59
  14. package/dist/direct/hub-host.js.map +1 -1
  15. package/dist/direct/hub-local.d.ts +22 -0
  16. package/dist/direct/hub-local.js +46 -0
  17. package/dist/direct/hub-local.js.map +1 -0
  18. package/dist/direct/hub.d.ts +37 -10
  19. package/dist/direct/hub.js +114 -23
  20. package/dist/direct/hub.js.map +1 -1
  21. package/dist/direct/index.d.ts +37 -4
  22. package/dist/direct/index.js +162 -21
  23. package/dist/direct/index.js.map +1 -1
  24. package/dist/direct/metrics.d.ts +12 -0
  25. package/dist/direct/metrics.js +45 -0
  26. package/dist/direct/metrics.js.map +1 -0
  27. package/dist/direct/portmap.d.ts +9 -2
  28. package/dist/direct/portmap.js +7 -2
  29. package/dist/direct/portmap.js.map +1 -1
  30. package/dist/direct/sha1.d.ts +16 -0
  31. package/dist/direct/sha1.js +111 -0
  32. package/dist/direct/sha1.js.map +1 -0
  33. package/dist/direct-address.d.ts +2 -2
  34. package/dist/direct-address.js.map +1 -1
  35. package/dist/friendly.d.ts +28 -0
  36. package/dist/friendly.js +40 -0
  37. package/dist/friendly.js.map +1 -1
  38. package/dist/index.d.ts +11 -3
  39. package/dist/index.js +8 -2
  40. package/dist/index.js.map +1 -1
  41. package/dist/internals.d.ts +9 -0
  42. package/dist/internals.js +9 -0
  43. package/dist/internals.js.map +1 -1
  44. package/dist/join-code-kind.d.ts +10 -0
  45. package/dist/join-code-kind.js +10 -0
  46. package/dist/join-code-kind.js.map +1 -0
  47. package/dist/joincode.d.ts +10 -1
  48. package/dist/joincode.js +23 -3
  49. package/dist/joincode.js.map +1 -1
  50. package/dist/lobby.d.ts +65 -4
  51. package/dist/lobby.js +151 -12
  52. package/dist/lobby.js.map +1 -1
  53. package/dist/lockstep.d.ts +8 -0
  54. package/dist/lockstep.js +23 -2
  55. package/dist/lockstep.js.map +1 -1
  56. package/dist/nostr/crypto.d.ts +20 -0
  57. package/dist/nostr/crypto.js +78 -0
  58. package/dist/nostr/crypto.js.map +1 -0
  59. package/dist/nostr/host.d.ts +66 -0
  60. package/dist/nostr/host.js +196 -0
  61. package/dist/nostr/host.js.map +1 -0
  62. package/dist/nostr/limits.d.ts +31 -0
  63. package/dist/nostr/limits.js +36 -0
  64. package/dist/nostr/limits.js.map +1 -0
  65. package/dist/nostr/link.d.ts +70 -0
  66. package/dist/nostr/link.js +180 -0
  67. package/dist/nostr/link.js.map +1 -0
  68. package/dist/nostr/relays.d.ts +79 -0
  69. package/dist/nostr/relays.js +315 -0
  70. package/dist/nostr/relays.js.map +1 -0
  71. package/dist/nostr/room.d.ts +43 -0
  72. package/dist/nostr/room.js +104 -0
  73. package/dist/nostr/room.js.map +1 -0
  74. package/dist/nostr/secp256k1.d.ts +10 -0
  75. package/dist/nostr/secp256k1.js +171 -0
  76. package/dist/nostr/secp256k1.js.map +1 -0
  77. package/dist/nostr/socket.d.ts +48 -0
  78. package/dist/nostr/socket.js +141 -0
  79. package/dist/nostr/socket.js.map +1 -0
  80. package/dist/onelobby.bundle.js +146 -0
  81. package/dist/onelobby.bundle.js.map +7 -0
  82. package/dist/onelobby.bundle.mjs +11 -5
  83. package/dist/onelobby.bundle.mjs.map +4 -4
  84. package/dist/onelobby.d.ts +54 -4
  85. package/dist/onelobby.global.js +2932 -49
  86. package/dist/onelobby.global.js.map +4 -4
  87. package/dist/onelobby.global.min.js +11 -5
  88. package/dist/onelobby.global.min.js.map +4 -4
  89. package/dist/onelobby.js +158 -15
  90. package/dist/onelobby.js.map +1 -1
  91. package/dist/pause.d.ts +85 -0
  92. package/dist/pause.js +160 -0
  93. package/dist/pause.js.map +1 -0
  94. package/dist/transport.d.ts +3 -1
  95. package/dist/transport.js.map +1 -1
  96. package/dist/webrtc.d.ts +1 -0
  97. package/dist/webrtc.js +6 -1
  98. package/dist/webrtc.js.map +1 -1
  99. package/package.json +4 -2
package/README.md CHANGED
@@ -9,7 +9,7 @@ You need a OneLobby lobby server (the WebSocket URL, like `wss://lobby.example.c
9
9
  ## Use it from a `<script>` tag (no build tools)
10
10
 
11
11
  ```html
12
- <script src="https://cdn.jsdelivr.net/npm/onelobby@0.4.1/dist/onelobby.global.min.js"></script>
12
+ <script src="https://cdn.jsdelivr.net/npm/onelobby@0.6.0/dist/onelobby.global.min.js"></script>
13
13
  <script>
14
14
  const kit = new OneLobby.OneLobby({
15
15
  gameId: "com.you.yourgame", // any unique name for your game
@@ -51,7 +51,7 @@ const lobby = await kit.host({ name: "My game", maxPlayers: 4 });
51
51
  console.log("Tell your friend this code:", formatCode(lobby.code));
52
52
  ```
53
53
 
54
- Without a bundler: `import { OneLobby } from "https://cdn.jsdelivr.net/npm/onelobby@0.4.1/dist/onelobby.bundle.mjs"`.
54
+ Without a bundler: `import { OneLobby } from "https://cdn.jsdelivr.net/npm/onelobby@0.6.0/dist/onelobby.bundle.mjs"`.
55
55
 
56
56
  ## Made for players
57
57
 
@@ -0,0 +1,244 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/ParkerTonra/onelobby/spec/blueprint.schema.json",
4
+ "title": "onelobby.json (OneLobby blueprint)",
5
+ "description": "One settings file for a game's multiplayer: the agent skill and create-onelobby write it, OneLobby.fromConfig (JS) and Blueprint.Load (C#) read it. Design: docs/design/blueprint.md. Every key but game.id has a default. \"$comment\" keys are allowed anywhere and ignored. Cross-key rules are listed in x-rules; the loaders enforce them with the same rule ids (spec/vectors/blueprint.json).",
6
+ "type": "object",
7
+ "required": ["game"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "$schema": { "type": "string" },
11
+ "$comment": { "type": "string" },
12
+ "version": { "description": "Blueprint format version.", "const": 1, "default": 1 },
13
+ "game": {
14
+ "type": "object",
15
+ "required": ["id"],
16
+ "additionalProperties": false,
17
+ "properties": {
18
+ "$comment": { "type": "string" },
19
+ "id": {
20
+ "description": "Reverse-DNS id. Lobbies only match players of the same game.",
21
+ "type": "string",
22
+ "pattern": "^[A-Za-z0-9._-]{1,64}$"
23
+ },
24
+ "netVersion": {
25
+ "description": "Bump whenever command bytes or rules change; mismatched versions cannot join each other.",
26
+ "type": "string",
27
+ "minLength": 1,
28
+ "maxLength": 32,
29
+ "default": "1"
30
+ },
31
+ "title": { "description": "Shown in the generated UI; defaults to the folder name.", "type": "string", "maxLength": 64 }
32
+ }
33
+ },
34
+ "engine": {
35
+ "description": "Picks the recipe, package and transport. Shapes the defaults of hosting.direct, hosting.server, lobby.joinLink and ui.kind.",
36
+ "enum": ["web", "node-browser", "godot-csharp", "unity", "dotnet"]
37
+ },
38
+ "hosting": {
39
+ "type": "object",
40
+ "additionalProperties": false,
41
+ "properties": {
42
+ "$comment": { "type": "string" },
43
+ "direct": {
44
+ "description": "Players host from their own machine with a join code: keeps working after any server is gone. Default: true unless engine is web (a browser cannot listen on a port).",
45
+ "type": "boolean"
46
+ },
47
+ "server": {
48
+ "description": "Lobbies through a lobby server (codes, public list, TURN relay). Default: true for engine web, false otherwise.",
49
+ "type": "boolean"
50
+ },
51
+ "lobbyServer": {
52
+ "description": "Default lobby server URL (ws:// or wss://). A setting, never a constant: players can override it.",
53
+ "type": "string",
54
+ "default": ""
55
+ },
56
+ "directPort": { "description": "Port a direct host listens on (falls back up to +9 when taken).", "type": "integer", "minimum": 1024, "maximum": 65535, "default": 47810 },
57
+ "openPort": { "description": "Ask the router to open the direct port (UPnP / NAT-PMP / PCP).", "type": "boolean", "default": true },
58
+ "nostr": { "description": "Direct host: also hand out a Nostr code (version 1) that works through public Nostr relays, no port forwarding needed. Off: IP codes only.", "type": "boolean", "default": true },
59
+ "nostrRelays": {
60
+ "description": "Nostr relay URLs (wss://) for the Nostr code. Default: the library's list, probed with checkNostrRelays when the blueprint was written.",
61
+ "type": "array",
62
+ "items": { "type": "string" },
63
+ "default": ["wss://nos.lol", "wss://relay.primal.net", "wss://nostr-pub.wellorder.net", "wss://nostr.oxtr.dev", "wss://nostr.bitcoiner.social", "wss://relay.nostr.net", "wss://nostr.sathoarder.com"]
64
+ },
65
+ "relayOnly": { "description": "Server lobbies only: force TURN so players never see each other's IP.", "type": "boolean", "default": false },
66
+ "allowedOrigins": {
67
+ "description": "Extra page origins a direct host accepts browser joiners from.",
68
+ "type": "array",
69
+ "items": { "type": "string" },
70
+ "default": []
71
+ }
72
+ }
73
+ },
74
+ "lobby": {
75
+ "type": "object",
76
+ "additionalProperties": false,
77
+ "properties": {
78
+ "$comment": { "type": "string" },
79
+ "maxPlayers": { "description": "Seats, host included.", "type": "integer", "minimum": 2, "maximum": 64, "default": 4 },
80
+ "listed": { "description": "Show hosted lobbies in a public Browse list. Off: join by code or link only.", "type": "boolean", "default": false },
81
+ "approveJoins": { "description": "The host approves each joiner. Cannot be combined with listed.", "type": "boolean", "default": false },
82
+ "password": { "description": "The game asks the host for a lobby password.", "type": "boolean", "default": false },
83
+ "lateJoin": { "description": "Players may join a game already in progress.", "type": "boolean", "default": true },
84
+ "rejoin": { "description": "A player who drops or reloads takes their old seat back.", "type": "boolean", "default": true },
85
+ "joinLink": { "description": "Offer a shareable join link. Default: true for engine web and node-browser.", "type": "boolean" },
86
+ "meta": {
87
+ "description": "Lobby key/value strings: at most 16 keys, keys and values at most 64 characters.",
88
+ "type": "object",
89
+ "maxProperties": 16,
90
+ "propertyNames": { "maxLength": 64 },
91
+ "additionalProperties": { "type": "string", "maxLength": 64 },
92
+ "default": {}
93
+ }
94
+ }
95
+ },
96
+ "sync": {
97
+ "type": "object",
98
+ "additionalProperties": false,
99
+ "properties": {
100
+ "$comment": { "type": "string" },
101
+ "mode": {
102
+ "description": "lockstep: deterministic sim, host orders commands. raw: you send bytes. replication: host-authoritative snapshots (C# only).",
103
+ "enum": ["raw", "lockstep", "replication"],
104
+ "default": "raw"
105
+ },
106
+ "tickRate": { "description": "Simulation ticks per second.", "type": "integer", "minimum": 1, "maximum": 120, "default": 20 },
107
+ "hashEveryTicks": { "description": "Lockstep: compare state hashes this often. Default: tickRate (once a second).", "type": "integer", "minimum": 1, "maximum": 100000 }
108
+ }
109
+ },
110
+ "players": {
111
+ "type": "object",
112
+ "additionalProperties": false,
113
+ "properties": {
114
+ "$comment": { "type": "string" },
115
+ "identity": {
116
+ "description": "name: players type a name. anonymous: no names, everyone is Player N. platform: the platform name (Steam), else name.",
117
+ "enum": ["name", "anonymous", "platform"],
118
+ "default": "name"
119
+ },
120
+ "maxNameLength": { "description": "Longest display name accepted.", "type": "integer", "minimum": 1, "maximum": 24, "default": 24 }
121
+ }
122
+ },
123
+ "chat": {
124
+ "type": "object",
125
+ "additionalProperties": false,
126
+ "properties": {
127
+ "$comment": { "type": "string" },
128
+ "text": { "description": "Text chat. Players can talk to strangers: a rating disclosure.", "type": "boolean", "default": true },
129
+ "voice": { "description": "Voice chat (asks for the microphone). Off unless the game has a reason for it.", "type": "boolean", "default": false },
130
+ "voiceMode": { "description": "How voice transmits.", "enum": ["pushToTalk", "voiceActivity"], "default": "pushToTalk" }
131
+ }
132
+ },
133
+ "session": {
134
+ "type": "object",
135
+ "additionalProperties": false,
136
+ "properties": {
137
+ "$comment": { "type": "string" },
138
+ "hostLeaves": {
139
+ "description": "end: the session ends for everyone. migrate: another player becomes host (server lobbies only).",
140
+ "enum": ["end", "migrate"],
141
+ "default": "end"
142
+ },
143
+ "pause": {
144
+ "description": "off: no network pause (lobby.Pause.Request / pause.request throws pause_disabled). host: only the host pauses and resumes. anyone: any player pauses; the pauser or the host resumes. A lobby made without a blueprint behaves as anyone.",
145
+ "enum": ["off", "host", "anyone"],
146
+ "default": "off"
147
+ },
148
+ "readyTimeoutMs": { "description": "A joiner that cannot reach the host gives up after this long. 0 waits forever.", "type": "integer", "minimum": 0, "maximum": 600000, "default": 15000 },
149
+ "peerTimeoutMs": {
150
+ "description": "Drop a silent peer after this long. 0 uses the built-in ~30 s; otherwise at least 6000.",
151
+ "type": "integer",
152
+ "anyOf": [{ "const": 0 }, { "minimum": 6000, "maximum": 600000 }],
153
+ "default": 0
154
+ },
155
+ "rateLimit": {
156
+ "description": "Cap each joiner's incoming raw messages; the excess is dropped.",
157
+ "type": "object",
158
+ "required": ["messages", "perMs"],
159
+ "additionalProperties": false,
160
+ "properties": {
161
+ "messages": { "type": "integer", "minimum": 1 },
162
+ "perMs": { "type": "integer", "minimum": 1 }
163
+ }
164
+ },
165
+ "runInBackground": { "description": "Keep networking while the window is unfocused.", "type": "boolean", "default": true }
166
+ }
167
+ },
168
+ "platforms": {
169
+ "description": "Platform adapters (blueprint s.9). Accepted today, not built yet.",
170
+ "type": "object",
171
+ "additionalProperties": false,
172
+ "properties": {
173
+ "$comment": { "type": "string" },
174
+ "steam": {
175
+ "default": false,
176
+ "anyOf": [
177
+ { "type": "boolean" },
178
+ { "type": "object", "properties": { "appId": { "type": "integer", "minimum": 1 } }, "additionalProperties": false }
179
+ ]
180
+ },
181
+ "epic": {
182
+ "default": false,
183
+ "anyOf": [
184
+ { "type": "boolean" },
185
+ { "type": "object", "properties": { "productId": { "type": "string" } }, "additionalProperties": { "type": "string" } }
186
+ ]
187
+ },
188
+ "discord": { "type": "boolean", "default": false }
189
+ }
190
+ },
191
+ "ui": {
192
+ "type": "object",
193
+ "additionalProperties": false,
194
+ "properties": {
195
+ "$comment": { "type": "string" },
196
+ "kind": {
197
+ "description": "drop-in: the <one-lobby> element (web). generated: menus written into the game. none: you build it. Default: drop-in for engine web, generated otherwise.",
198
+ "enum": ["drop-in", "generated", "none"]
199
+ },
200
+ "entry": { "description": "Where multiplayer lives in the game's menus, e.g. \"Main menu: Play together\".", "type": "string" },
201
+ "theme": { "description": "Drop-in only.", "enum": ["light", "dark", "auto"], "default": "auto" }
202
+ }
203
+ }
204
+ },
205
+ "allOf": [
206
+ {
207
+ "$comment": "rule hosting_none (path hosting): at least one of direct, server",
208
+ "not": { "required": ["hosting"], "properties": { "hosting": { "required": ["direct", "server"], "properties": { "direct": { "const": false }, "server": { "const": false } } } } }
209
+ },
210
+ {
211
+ "$comment": "rule listed_and_approve (path lobby): listed and approveJoins cannot both be true",
212
+ "not": { "required": ["lobby"], "properties": { "lobby": { "required": ["listed", "approveJoins"], "properties": { "listed": { "const": true }, "approveJoins": { "const": true } } } } }
213
+ },
214
+ {
215
+ "$comment": "rule direct_needs_listener (path hosting.direct): engine web cannot host direct",
216
+ "if": { "required": ["engine"], "properties": { "engine": { "const": "web" } } },
217
+ "then": { "not": { "required": ["hosting"], "properties": { "hosting": { "required": ["direct"], "properties": { "direct": { "const": true } } } } } }
218
+ },
219
+ {
220
+ "$comment": "rule replication_needs_csharp (path sync.mode): replication needs a C# engine (godot-csharp, unity, dotnet) and an explicit engine",
221
+ "if": { "required": ["sync"], "properties": { "sync": { "required": ["mode"], "properties": { "mode": { "const": "replication" } } } } },
222
+ "then": { "required": ["engine"], "properties": { "engine": { "enum": ["godot-csharp", "unity", "dotnet"] } } }
223
+ },
224
+ {
225
+ "$comment": "rule migrate_with_replication (path session.hostLeaves): replication has no host migration",
226
+ "not": {
227
+ "required": ["sync", "session"],
228
+ "properties": {
229
+ "sync": { "required": ["mode"], "properties": { "mode": { "const": "replication" } } },
230
+ "session": { "required": ["hostLeaves"], "properties": { "hostLeaves": { "const": "migrate" } } }
231
+ }
232
+ }
233
+ }
234
+ ],
235
+ "x-rules": {
236
+ "$comment": "Rules that depend on engine-dependent defaults cannot be written in plain JSON Schema; the loaders apply them after filling defaults.",
237
+ "hosting_none": { "path": "hosting", "message": "at least one of direct, server must be true" },
238
+ "listed_and_approve": { "path": "lobby", "message": "listed and approveJoins cannot both be true" },
239
+ "direct_needs_listener": { "path": "hosting.direct", "message": "engine web cannot host direct (a browser cannot listen on a port)" },
240
+ "replication_needs_csharp": { "path": "sync.mode", "message": "replication needs engine godot-csharp, unity or dotnet" },
241
+ "migrate_with_replication": { "path": "session.hostLeaves", "message": "replication has no host migration; use end" },
242
+ "migrate_needs_server": { "path": "session.hostLeaves", "message": "migrate needs hosting.server (direct lobbies always end)" }
243
+ }
244
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Blueprint v1: the `onelobby.json` settings file (docs/design/blueprint.md s.4, spec/blueprint.schema.json).
3
+ * `parseBlueprint` validates by hand (the package has no dependencies), fills the defaults and returns a frozen
4
+ * object. Errors carry the dotted `path` and the `rule` id the shared vectors (spec/vectors/blueprint.json) expect.
5
+ */
6
+ import { OneLobbyError } from "./signaling.js";
7
+ export type BlueprintEngine = "web" | "node-browser" | "godot-csharp" | "unity" | "dotnet";
8
+ export interface Blueprint {
9
+ readonly version: 1;
10
+ readonly game: {
11
+ readonly id: string;
12
+ readonly netVersion: string;
13
+ readonly title?: string;
14
+ };
15
+ readonly engine?: BlueprintEngine;
16
+ readonly hosting: {
17
+ readonly direct: boolean;
18
+ readonly server: boolean;
19
+ readonly lobbyServer: string;
20
+ readonly directPort: number;
21
+ readonly openPort: boolean;
22
+ readonly nostr: boolean;
23
+ readonly nostrRelays: readonly string[];
24
+ readonly relayOnly: boolean;
25
+ readonly allowedOrigins: readonly string[];
26
+ };
27
+ readonly lobby: {
28
+ readonly maxPlayers: number;
29
+ readonly listed: boolean;
30
+ readonly approveJoins: boolean;
31
+ readonly password: boolean;
32
+ readonly lateJoin: boolean;
33
+ readonly rejoin: boolean;
34
+ readonly joinLink: boolean;
35
+ readonly meta: Readonly<Record<string, string>>;
36
+ };
37
+ readonly sync: {
38
+ readonly mode: "raw" | "lockstep" | "replication";
39
+ readonly tickRate: number;
40
+ readonly hashEveryTicks: number;
41
+ };
42
+ readonly players: {
43
+ readonly identity: "name" | "anonymous" | "platform";
44
+ readonly maxNameLength: number;
45
+ };
46
+ readonly chat: {
47
+ readonly text: boolean;
48
+ readonly voice: boolean;
49
+ readonly voiceMode: "pushToTalk" | "voiceActivity";
50
+ };
51
+ readonly session: {
52
+ readonly hostLeaves: "end" | "migrate";
53
+ readonly pause: "off" | "host" | "anyone";
54
+ readonly readyTimeoutMs: number;
55
+ readonly peerTimeoutMs: number;
56
+ readonly rateLimit?: {
57
+ readonly messages: number;
58
+ readonly perMs: number;
59
+ };
60
+ readonly runInBackground: boolean;
61
+ };
62
+ readonly platforms: {
63
+ readonly steam: boolean | {
64
+ readonly appId?: number;
65
+ };
66
+ readonly epic: boolean | {
67
+ readonly [k: string]: string;
68
+ };
69
+ readonly discord: boolean;
70
+ };
71
+ readonly ui: {
72
+ readonly kind: "drop-in" | "generated" | "none";
73
+ readonly entry?: string;
74
+ readonly theme: "light" | "dark" | "auto";
75
+ };
76
+ }
77
+ /** What you may write: only `game.id` is required. */
78
+ export interface BlueprintInput {
79
+ $schema?: string;
80
+ $comment?: string;
81
+ version?: 1;
82
+ game: {
83
+ $comment?: string;
84
+ id: string;
85
+ netVersion?: string;
86
+ title?: string;
87
+ };
88
+ engine?: BlueprintEngine;
89
+ hosting?: {
90
+ $comment?: string;
91
+ direct?: boolean;
92
+ server?: boolean;
93
+ lobbyServer?: string;
94
+ directPort?: number;
95
+ openPort?: boolean;
96
+ nostr?: boolean;
97
+ nostrRelays?: string[];
98
+ relayOnly?: boolean;
99
+ allowedOrigins?: string[];
100
+ };
101
+ lobby?: {
102
+ $comment?: string;
103
+ maxPlayers?: number;
104
+ listed?: boolean;
105
+ approveJoins?: boolean;
106
+ password?: boolean;
107
+ lateJoin?: boolean;
108
+ rejoin?: boolean;
109
+ joinLink?: boolean;
110
+ meta?: Record<string, string>;
111
+ };
112
+ sync?: {
113
+ $comment?: string;
114
+ mode?: "raw" | "lockstep" | "replication";
115
+ tickRate?: number;
116
+ hashEveryTicks?: number;
117
+ };
118
+ players?: {
119
+ $comment?: string;
120
+ identity?: "name" | "anonymous" | "platform";
121
+ maxNameLength?: number;
122
+ };
123
+ chat?: {
124
+ $comment?: string;
125
+ text?: boolean;
126
+ voice?: boolean;
127
+ voiceMode?: "pushToTalk" | "voiceActivity";
128
+ };
129
+ session?: {
130
+ $comment?: string;
131
+ hostLeaves?: "end" | "migrate";
132
+ pause?: "off" | "host" | "anyone";
133
+ readyTimeoutMs?: number;
134
+ peerTimeoutMs?: number;
135
+ rateLimit?: {
136
+ messages: number;
137
+ perMs: number;
138
+ };
139
+ runInBackground?: boolean;
140
+ };
141
+ platforms?: {
142
+ $comment?: string;
143
+ steam?: boolean | {
144
+ appId?: number;
145
+ };
146
+ epic?: boolean | {
147
+ [k: string]: string;
148
+ };
149
+ discord?: boolean;
150
+ };
151
+ ui?: {
152
+ $comment?: string;
153
+ kind?: "drop-in" | "generated" | "none";
154
+ entry?: string;
155
+ theme?: "light" | "dark" | "auto";
156
+ };
157
+ }
158
+ /** Identity function for TypeScript completion in a `.ts` config (like Vite's `defineConfig`). */
159
+ export declare function defineBlueprint<T extends BlueprintInput>(x: T): T;
160
+ /** `bad_config`: the file is not a valid blueprint. `path` is dotted ("" = root), `rule` a vector rule id. */
161
+ export declare class BlueprintError extends OneLobbyError {
162
+ path: string;
163
+ rule: string;
164
+ constructor(path: string, rule: string, detail: string);
165
+ }
166
+ /**
167
+ * Validate `json` (an already-parsed onelobby.json), fill every default and return the frozen result.
168
+ * Throws a `BlueprintError` (`OneLobbyError` code `bad_config`, with `path` and `rule`) on the first problem.
169
+ */
170
+ export declare function parseBlueprint(json: unknown): Blueprint;