@lokiplay/sdk 0.1.1 → 0.2.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.
- package/README.md +36 -3
- package/dist/index.d.ts +48 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +394 -78
- package/dist/index.js.map +1 -1
- package/dist/synchronized-room.d.ts +103 -0
- package/dist/synchronized-room.d.ts.map +1 -0
- package/dist/synchronized-room.js +1029 -0
- package/dist/synchronized-room.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ JavaScript client SDK for authenticating players, joining Loki multiplayer
|
|
|
4
4
|
rooms, sending actions and events, and subscribing to server messages.
|
|
5
5
|
|
|
6
6
|
```sh
|
|
7
|
-
npm install @lokiplay/sdk
|
|
7
|
+
npm install @lokiplay/sdk@0.2.0
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
Use `FirstPartyTransport` for production. It defaults to
|
|
@@ -19,5 +19,38 @@ game manifest allowlist.
|
|
|
19
19
|
|
|
20
20
|
`LokiClient` supports rooms, invite resolution, matchmaking, actions, events,
|
|
21
21
|
host state, snapshots, presence, chat, private scores, token refresh, reconnect,
|
|
22
|
-
and host-migration messages.
|
|
23
|
-
|
|
22
|
+
and host-migration messages. Loki creates every room and issues the shareable
|
|
23
|
+
invite code. Call `createRoom()` and `joinRoom({ inviteCode })` and do not invent
|
|
24
|
+
room keys.
|
|
25
|
+
|
|
26
|
+
Prefer `createSynchronizedRoom()` for shared state. Supply opaque state, opaque
|
|
27
|
+
actions, and a reducer. Loki sequences actions, commits only from the current
|
|
28
|
+
authority, tracks `stateVersion`, deduplicates action IDs, restores snapshots,
|
|
29
|
+
and exposes membership and connection status. Low-level `sendAction()` and
|
|
30
|
+
`sendHostState()` remain available. Host authority is suitable for casual
|
|
31
|
+
unranked sessions, not ranked integrity.
|
|
32
|
+
|
|
33
|
+
Generic integration steps:
|
|
34
|
+
|
|
35
|
+
1. Install the exact published SDK version for this release.
|
|
36
|
+
2. Define this project's state and action schemas.
|
|
37
|
+
3. Define a synchronous, deterministic, JSON-compatible reducer.
|
|
38
|
+
4. Create or join with `create()` or `join({ inviteCode })`.
|
|
39
|
+
5. Subscribe to snapshots for state, members, authority, and connection.
|
|
40
|
+
6. Dispatch actions and wait for authoritative confirmation.
|
|
41
|
+
7. Render connection and `lastError` from the snapshot.
|
|
42
|
+
8. Handle rejected actions without inventing a parallel protocol.
|
|
43
|
+
9. Build and deploy from this project's own repository.
|
|
44
|
+
|
|
45
|
+
Reducers must return quickly. Loki clones reducer inputs and catches thrown
|
|
46
|
+
errors, but a synchronous infinite loop cannot be interrupted without changing
|
|
47
|
+
the reducer API. Do not perform I/O or rendering inside `reduce`. Reducers must
|
|
48
|
+
be synchronous, deterministic, and JSON-compatible. Only the current host runs
|
|
49
|
+
`reduce`; members wait for an authoritative `state` acknowledgement keyed by
|
|
50
|
+
`(senderId, actionId)`.
|
|
51
|
+
|
|
52
|
+
`create()`, `join({ inviteCode })`, `dispatch(action)`, `leave()`, and
|
|
53
|
+
`reconnect()` are the shared synchronized-room API. A failed `leave()` retains
|
|
54
|
+
the room identity in `leave_failed` and blocks a new join until `leave()`
|
|
55
|
+
succeeds or `close()` abandons the handle. 0.2.0 clients fail fast when a
|
|
56
|
+
snapshot does not advertise `capabilities.synchronized_rooms`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,33 +1,41 @@
|
|
|
1
1
|
import { type ClientEnvelope, type ServerEnvelope } from "@lokiplay/protocol";
|
|
2
|
+
import { SynchronizedRoom, type ConnectionEvent, type SynchronizedRoomOptions } from "./synchronized-room.js";
|
|
3
|
+
export { SYNCHRONIZED_ROOM_ACTION_TTL_MS, SYNCHRONIZED_ROOM_COMMIT_TIMEOUT_MS, SYNCHRONIZED_ROOM_MAX_MESSAGE_BYTES, SYNCHRONIZED_ROOM_MAX_PENDING, SYNCHRONIZED_ROOM_MAX_RECENT_ACTIONS, SYNCHRONIZED_ROOM_MAX_REDUCER_MS, SynchronizedRoom, SynchronizedRoomError, } from "./synchronized-room.js";
|
|
4
|
+
export type { ActionContext, CommittedTransition, ConnectionEvent, ConnectionState, RoomMember, Schema, SynchronizedRoomOutcome, SynchronizedRoomSnapshot, } from "./synchronized-room.js";
|
|
2
5
|
export declare const LOKI_API_ORIGIN = "https://api.lokiplay.cc";
|
|
6
|
+
export type JoinedRoom = {
|
|
7
|
+
roomId: string;
|
|
8
|
+
inviteCode: string;
|
|
9
|
+
snapshot: ServerEnvelope;
|
|
10
|
+
};
|
|
11
|
+
export declare function lokiErrorFromUnknown(error: unknown): Promise<Error>;
|
|
3
12
|
export interface LokiTransport {
|
|
4
13
|
authenticate(token: string): Promise<{
|
|
5
14
|
playerId: string;
|
|
6
15
|
}>;
|
|
16
|
+
createRoom(input: {
|
|
17
|
+
projectId: string;
|
|
18
|
+
}): Promise<JoinedRoom>;
|
|
7
19
|
joinRoom(input: {
|
|
8
20
|
projectId: string;
|
|
9
|
-
|
|
10
|
-
}): Promise<
|
|
11
|
-
roomId: string;
|
|
12
|
-
snapshot: ServerEnvelope;
|
|
13
|
-
}>;
|
|
21
|
+
inviteCode: string;
|
|
22
|
+
}): Promise<JoinedRoom>;
|
|
14
23
|
send(message: ClientEnvelope): Promise<void>;
|
|
15
24
|
subscribe(listener: (message: unknown) => void): () => void;
|
|
16
25
|
resolveInvite?(inviteCode: string): Promise<{
|
|
17
26
|
roomId: string;
|
|
27
|
+
inviteCode: string;
|
|
18
28
|
}>;
|
|
19
29
|
matchmake?(input: {
|
|
20
30
|
minPlayers: number;
|
|
21
31
|
maxPlayers: number;
|
|
22
32
|
teamSize?: number;
|
|
23
|
-
}): Promise<
|
|
24
|
-
roomId: string;
|
|
25
|
-
snapshot: ServerEnvelope;
|
|
26
|
-
}>;
|
|
33
|
+
}): Promise<JoinedRoom>;
|
|
27
34
|
leaveRoom?(roomId: string): Promise<void>;
|
|
28
35
|
reconnect?(): Promise<void>;
|
|
29
36
|
refresh?(): Promise<void>;
|
|
30
37
|
close(): Promise<void>;
|
|
38
|
+
subscribeConnection?(listener: (event: ConnectionEvent) => void): () => void;
|
|
31
39
|
}
|
|
32
40
|
export interface LokiClientOptions {
|
|
33
41
|
projectId: string;
|
|
@@ -36,26 +44,44 @@ export interface LokiClientOptions {
|
|
|
36
44
|
export declare class LokiClient {
|
|
37
45
|
#private;
|
|
38
46
|
constructor(options: LokiClientOptions);
|
|
47
|
+
get playerId(): string | undefined;
|
|
48
|
+
get roomId(): string | undefined;
|
|
49
|
+
get inviteCode(): string;
|
|
39
50
|
initialize(): void;
|
|
51
|
+
createSynchronizedRoom<State, Action>(options: SynchronizedRoomOptions<State, Action>): SynchronizedRoom<State, Action>;
|
|
52
|
+
onConnection(listener: (event: ConnectionEvent) => void): () => void;
|
|
40
53
|
authenticate(token: string): Promise<{
|
|
41
54
|
playerId: string;
|
|
42
55
|
}>;
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
createRoom(): Promise<JoinedRoom>;
|
|
57
|
+
joinRoom(input: {
|
|
58
|
+
inviteCode: string;
|
|
59
|
+
}): Promise<JoinedRoom>;
|
|
60
|
+
sendAction(payload: unknown, options?: {
|
|
61
|
+
actionId?: string;
|
|
62
|
+
}): Promise<void>;
|
|
45
63
|
sendEvent(payload: unknown, reliable?: boolean): Promise<void>;
|
|
46
|
-
sendHostState(expectedVersion: number, state: unknown
|
|
64
|
+
sendHostState(expectedVersion: number, state: unknown, options?: {
|
|
65
|
+
actionId?: string;
|
|
66
|
+
expectedStateVersion?: number;
|
|
67
|
+
senderId?: string;
|
|
68
|
+
}): Promise<void>;
|
|
69
|
+
sendActionRejection(actionId: string, outcome: "rejected" | "invalid", message: string, options?: {
|
|
70
|
+
senderId?: string;
|
|
71
|
+
}): Promise<void>;
|
|
47
72
|
requestSnapshot(): Promise<void>;
|
|
48
73
|
sendChat(text: string, channel?: "lobby" | "match"): Promise<void>;
|
|
49
74
|
submitScore(leaderboardId: string, score: number, subscore?: number): Promise<void>;
|
|
50
75
|
resolveInvite(inviteCode: string): Promise<{
|
|
51
76
|
roomId: string;
|
|
77
|
+
inviteCode: string;
|
|
52
78
|
}>;
|
|
53
79
|
matchmake(input: {
|
|
54
80
|
minPlayers: number;
|
|
55
81
|
maxPlayers: number;
|
|
56
82
|
teamSize?: number;
|
|
57
|
-
}): Promise<
|
|
58
|
-
leaveRoom(): Promise<void>;
|
|
83
|
+
}): Promise<JoinedRoom>;
|
|
84
|
+
leaveRoom(roomId?: string): Promise<void>;
|
|
59
85
|
reconnect(): Promise<void>;
|
|
60
86
|
refresh(): Promise<void>;
|
|
61
87
|
onMessage(listener: (message: ServerEnvelope) => void): () => void;
|
|
@@ -80,26 +106,25 @@ export declare class FirstPartyTransport implements LokiTransport {
|
|
|
80
106
|
authenticate(token: string): Promise<{
|
|
81
107
|
playerId: string;
|
|
82
108
|
}>;
|
|
109
|
+
createRoom(_input: {
|
|
110
|
+
projectId: string;
|
|
111
|
+
}): Promise<JoinedRoom>;
|
|
83
112
|
joinRoom(input: {
|
|
84
113
|
projectId: string;
|
|
85
|
-
|
|
86
|
-
}): Promise<
|
|
87
|
-
roomId: string;
|
|
88
|
-
snapshot: ServerEnvelope;
|
|
89
|
-
}>;
|
|
114
|
+
inviteCode: string;
|
|
115
|
+
}): Promise<JoinedRoom>;
|
|
90
116
|
resolveInvite(inviteCode: string): Promise<{
|
|
91
117
|
roomId: string;
|
|
118
|
+
inviteCode: string;
|
|
92
119
|
}>;
|
|
93
120
|
matchmake(input: {
|
|
94
121
|
minPlayers: number;
|
|
95
122
|
maxPlayers: number;
|
|
96
123
|
teamSize?: number;
|
|
97
|
-
}): Promise<
|
|
98
|
-
roomId: string;
|
|
99
|
-
snapshot: ServerEnvelope;
|
|
100
|
-
}>;
|
|
124
|
+
}): Promise<JoinedRoom>;
|
|
101
125
|
send(message: ClientEnvelope): Promise<void>;
|
|
102
126
|
subscribe(listener: (message: unknown) => void): () => void;
|
|
127
|
+
subscribeConnection(listener: (event: ConnectionEvent) => void): () => void;
|
|
103
128
|
leaveRoom(roomId: string): Promise<void>;
|
|
104
129
|
refresh(): Promise<void>;
|
|
105
130
|
reconnect(): Promise<void>;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|