@watchtower-sdk/core 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 +336 -0
- package/dist/index.d.mts +296 -0
- package/dist/index.d.ts +296 -0
- package/dist/index.js +467 -0
- package/dist/index.mjs +441 -0
- package/package.json +49 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watchtower SDK
|
|
3
|
+
* Simple game backend - saves, multiplayer rooms, and more
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { Watchtower } from '@watchtower/sdk'
|
|
8
|
+
*
|
|
9
|
+
* const wt = new Watchtower({ gameId: 'my-game', apiKey: 'wt_...' })
|
|
10
|
+
*
|
|
11
|
+
* // Cloud saves
|
|
12
|
+
* await wt.save('progress', { level: 5, coins: 100 })
|
|
13
|
+
* const data = await wt.load('progress')
|
|
14
|
+
*
|
|
15
|
+
* // Multiplayer
|
|
16
|
+
* const room = await wt.createRoom()
|
|
17
|
+
* console.log('Room code:', room.code) // e.g., "ABCD"
|
|
18
|
+
*
|
|
19
|
+
* // Player state (auto-synced to others)
|
|
20
|
+
* room.player.set({ x: 100, y: 200, sprite: 'idle' })
|
|
21
|
+
*
|
|
22
|
+
* // See other players
|
|
23
|
+
* room.on('players', (players) => {
|
|
24
|
+
* for (const [id, state] of Object.entries(players)) {
|
|
25
|
+
* updatePlayer(id, state)
|
|
26
|
+
* }
|
|
27
|
+
* })
|
|
28
|
+
*
|
|
29
|
+
* // Shared game state (host-controlled)
|
|
30
|
+
* if (room.isHost) {
|
|
31
|
+
* room.state.set({ phase: 'playing', round: 1 })
|
|
32
|
+
* }
|
|
33
|
+
* room.on('state', (state) => updateGameState(state))
|
|
34
|
+
*
|
|
35
|
+
* // One-off events
|
|
36
|
+
* room.broadcast({ type: 'explosion', x: 50, y: 50 })
|
|
37
|
+
* room.on('message', (from, data) => handleEvent(from, data))
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
interface WatchtowerConfig {
|
|
41
|
+
/** Your game's unique identifier */
|
|
42
|
+
gameId: string;
|
|
43
|
+
/** Player ID (auto-generated if not provided) */
|
|
44
|
+
playerId?: string;
|
|
45
|
+
/** API base URL (default: https://watchtower-api.watchtower-host.workers.dev) */
|
|
46
|
+
apiUrl?: string;
|
|
47
|
+
/** API key for authenticated requests (optional for now) */
|
|
48
|
+
apiKey?: string;
|
|
49
|
+
}
|
|
50
|
+
interface SaveData<T = unknown> {
|
|
51
|
+
key: string;
|
|
52
|
+
data: T;
|
|
53
|
+
}
|
|
54
|
+
interface PlayerInfo {
|
|
55
|
+
id: string;
|
|
56
|
+
joinedAt: number;
|
|
57
|
+
}
|
|
58
|
+
interface RoomInfo {
|
|
59
|
+
code: string;
|
|
60
|
+
gameId: string;
|
|
61
|
+
hostId: string;
|
|
62
|
+
players: PlayerInfo[];
|
|
63
|
+
playerCount: number;
|
|
64
|
+
}
|
|
65
|
+
/** Game-wide stats */
|
|
66
|
+
interface GameStats {
|
|
67
|
+
/** Players currently online */
|
|
68
|
+
online: number;
|
|
69
|
+
/** Unique players today (DAU) */
|
|
70
|
+
today: number;
|
|
71
|
+
/** Unique players this month (MAU) */
|
|
72
|
+
monthly: number;
|
|
73
|
+
/** Total unique players all time */
|
|
74
|
+
total: number;
|
|
75
|
+
/** Currently active rooms */
|
|
76
|
+
rooms: number;
|
|
77
|
+
/** Players currently in multiplayer rooms */
|
|
78
|
+
inRooms: number;
|
|
79
|
+
/** Average session length in seconds */
|
|
80
|
+
avgSession: number;
|
|
81
|
+
/** Average players per room */
|
|
82
|
+
avgRoomSize: number;
|
|
83
|
+
/** Last update timestamp */
|
|
84
|
+
updatedAt: number | null;
|
|
85
|
+
}
|
|
86
|
+
/** Current player's stats */
|
|
87
|
+
interface PlayerStats {
|
|
88
|
+
/** When the player first connected */
|
|
89
|
+
firstSeen: string | null;
|
|
90
|
+
/** When the player last connected */
|
|
91
|
+
lastSeen: string | null;
|
|
92
|
+
/** Total sessions */
|
|
93
|
+
sessions: number;
|
|
94
|
+
/** Total playtime in seconds */
|
|
95
|
+
playtime: number;
|
|
96
|
+
}
|
|
97
|
+
/** Player state - position, animation, custom data */
|
|
98
|
+
type PlayerState = Record<string, unknown>;
|
|
99
|
+
/** All players' states indexed by player ID */
|
|
100
|
+
type PlayersState = Record<string, PlayerState>;
|
|
101
|
+
/** Shared game state - host controlled */
|
|
102
|
+
type GameState = Record<string, unknown>;
|
|
103
|
+
type RoomEventMap = {
|
|
104
|
+
/** Fired when connected to room */
|
|
105
|
+
connected: (info: {
|
|
106
|
+
playerId: string;
|
|
107
|
+
room: RoomInfo;
|
|
108
|
+
}) => void;
|
|
109
|
+
/** Fired when a player joins */
|
|
110
|
+
playerJoined: (playerId: string, playerCount: number) => void;
|
|
111
|
+
/** Fired when a player leaves */
|
|
112
|
+
playerLeft: (playerId: string, playerCount: number) => void;
|
|
113
|
+
/** Fired when players' states update (includes all players) */
|
|
114
|
+
players: (players: PlayersState) => void;
|
|
115
|
+
/** Fired when shared game state updates */
|
|
116
|
+
state: (state: GameState) => void;
|
|
117
|
+
/** Fired when host changes */
|
|
118
|
+
hostChanged: (newHostId: string) => void;
|
|
119
|
+
/** Fired when receiving a broadcast message */
|
|
120
|
+
message: (from: string, data: unknown) => void;
|
|
121
|
+
/** Fired on disconnect */
|
|
122
|
+
disconnected: () => void;
|
|
123
|
+
/** Fired on error */
|
|
124
|
+
error: (error: Error) => void;
|
|
125
|
+
};
|
|
126
|
+
declare class PlayerStateManager {
|
|
127
|
+
private room;
|
|
128
|
+
private _state;
|
|
129
|
+
private syncInterval;
|
|
130
|
+
private dirty;
|
|
131
|
+
private syncRateMs;
|
|
132
|
+
constructor(room: Room, syncRateMs?: number);
|
|
133
|
+
/** Set player state (merged with existing) */
|
|
134
|
+
set(state: PlayerState): void;
|
|
135
|
+
/** Replace entire player state */
|
|
136
|
+
replace(state: PlayerState): void;
|
|
137
|
+
/** Get current player state */
|
|
138
|
+
get(): PlayerState;
|
|
139
|
+
/** Clear player state */
|
|
140
|
+
clear(): void;
|
|
141
|
+
/** Start automatic sync */
|
|
142
|
+
startSync(): void;
|
|
143
|
+
/** Stop automatic sync */
|
|
144
|
+
stopSync(): void;
|
|
145
|
+
/** Force immediate sync */
|
|
146
|
+
sync(): void;
|
|
147
|
+
}
|
|
148
|
+
declare class GameStateManager {
|
|
149
|
+
private room;
|
|
150
|
+
private _state;
|
|
151
|
+
constructor(room: Room);
|
|
152
|
+
/** Set game state (host only, merged with existing) */
|
|
153
|
+
set(state: GameState): void;
|
|
154
|
+
/** Replace entire game state (host only) */
|
|
155
|
+
replace(state: GameState): void;
|
|
156
|
+
/** Get current game state */
|
|
157
|
+
get(): GameState;
|
|
158
|
+
/** Update internal state (called on sync from server) */
|
|
159
|
+
_update(state: GameState): void;
|
|
160
|
+
}
|
|
161
|
+
declare class Room {
|
|
162
|
+
readonly code: string;
|
|
163
|
+
private ws;
|
|
164
|
+
private listeners;
|
|
165
|
+
private config;
|
|
166
|
+
/** Player state manager - set your position/state here */
|
|
167
|
+
readonly player: PlayerStateManager;
|
|
168
|
+
/** Game state manager - shared state (host-controlled) */
|
|
169
|
+
readonly state: GameStateManager;
|
|
170
|
+
/** All players' current states */
|
|
171
|
+
private _players;
|
|
172
|
+
/** Current host ID */
|
|
173
|
+
private _hostId;
|
|
174
|
+
/** Room info from initial connection */
|
|
175
|
+
private _roomInfo;
|
|
176
|
+
constructor(code: string, config: Required<WatchtowerConfig>);
|
|
177
|
+
/** Get the current host ID */
|
|
178
|
+
get hostId(): string;
|
|
179
|
+
/** Check if current player is the host */
|
|
180
|
+
get isHost(): boolean;
|
|
181
|
+
/** Get current player's ID */
|
|
182
|
+
get playerId(): string;
|
|
183
|
+
/** Get all players' states */
|
|
184
|
+
get players(): PlayersState;
|
|
185
|
+
/** Get player count */
|
|
186
|
+
get playerCount(): number;
|
|
187
|
+
/** Connect to the room via WebSocket */
|
|
188
|
+
connect(): Promise<void>;
|
|
189
|
+
private handleMessage;
|
|
190
|
+
/** Subscribe to room events */
|
|
191
|
+
on<K extends keyof RoomEventMap>(event: K, callback: RoomEventMap[K]): void;
|
|
192
|
+
/** Unsubscribe from room events */
|
|
193
|
+
off<K extends keyof RoomEventMap>(event: K, callback: RoomEventMap[K]): void;
|
|
194
|
+
private emit;
|
|
195
|
+
/** Broadcast data to all players in the room (for one-off events) */
|
|
196
|
+
broadcast(data: unknown, excludeSelf?: boolean): void;
|
|
197
|
+
/** Send data to a specific player */
|
|
198
|
+
sendTo(playerId: string, data: unknown): void;
|
|
199
|
+
/** Send a ping to measure latency */
|
|
200
|
+
ping(): void;
|
|
201
|
+
/** Request host transfer (host only) */
|
|
202
|
+
transferHost(newHostId: string): void;
|
|
203
|
+
private send;
|
|
204
|
+
/** Disconnect from the room */
|
|
205
|
+
disconnect(): void;
|
|
206
|
+
/** Check if connected */
|
|
207
|
+
get connected(): boolean;
|
|
208
|
+
}
|
|
209
|
+
declare class Watchtower {
|
|
210
|
+
/** @internal - Config is non-enumerable to prevent accidental API key exposure */
|
|
211
|
+
private readonly config;
|
|
212
|
+
constructor(config: WatchtowerConfig);
|
|
213
|
+
private generatePlayerId;
|
|
214
|
+
/** Get the current player ID */
|
|
215
|
+
get playerId(): string;
|
|
216
|
+
/** Get the game ID */
|
|
217
|
+
get gameId(): string;
|
|
218
|
+
private fetch;
|
|
219
|
+
/**
|
|
220
|
+
* Save data to the cloud
|
|
221
|
+
* @param key - Save slot name (e.g., "progress", "settings")
|
|
222
|
+
* @param data - Any JSON-serializable data
|
|
223
|
+
*/
|
|
224
|
+
save(key: string, data: unknown): Promise<void>;
|
|
225
|
+
/**
|
|
226
|
+
* Load data from the cloud
|
|
227
|
+
* @param key - Save slot name
|
|
228
|
+
* @returns The saved data, or null if not found
|
|
229
|
+
*/
|
|
230
|
+
load<T = unknown>(key: string): Promise<T | null>;
|
|
231
|
+
/**
|
|
232
|
+
* List all save keys for this player
|
|
233
|
+
*/
|
|
234
|
+
listSaves(): Promise<string[]>;
|
|
235
|
+
/**
|
|
236
|
+
* Delete a save
|
|
237
|
+
* @param key - Save slot name
|
|
238
|
+
*/
|
|
239
|
+
deleteSave(key: string): Promise<void>;
|
|
240
|
+
/**
|
|
241
|
+
* Create a new multiplayer room
|
|
242
|
+
* @returns A Room instance (already connected)
|
|
243
|
+
*/
|
|
244
|
+
createRoom(): Promise<Room>;
|
|
245
|
+
/**
|
|
246
|
+
* Join an existing room by code
|
|
247
|
+
* @param code - The 4-letter room code
|
|
248
|
+
* @returns A Room instance (already connected)
|
|
249
|
+
*/
|
|
250
|
+
joinRoom(code: string): Promise<Room>;
|
|
251
|
+
/**
|
|
252
|
+
* Get info about a room without joining
|
|
253
|
+
* @param code - The 4-letter room code
|
|
254
|
+
*/
|
|
255
|
+
getRoomInfo(code: string): Promise<RoomInfo>;
|
|
256
|
+
/**
|
|
257
|
+
* Get game-wide stats
|
|
258
|
+
* @returns Stats like online players, DAU, rooms active, etc.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```ts
|
|
262
|
+
* const stats = await wt.getStats()
|
|
263
|
+
* console.log(`${stats.online} players online`)
|
|
264
|
+
* console.log(`${stats.rooms} active rooms`)
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
getStats(): Promise<GameStats>;
|
|
268
|
+
/**
|
|
269
|
+
* Get the current player's stats
|
|
270
|
+
* @returns Player's firstSeen, sessions count, playtime
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```ts
|
|
274
|
+
* const me = await wt.getPlayerStats()
|
|
275
|
+
* console.log(`You've played ${Math.floor(me.playtime / 3600)} hours`)
|
|
276
|
+
* console.log(`Member since ${new Date(me.firstSeen).toLocaleDateString()}`)
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
getPlayerStats(): Promise<PlayerStats>;
|
|
280
|
+
/**
|
|
281
|
+
* Track a session start (call on game load)
|
|
282
|
+
* This is called automatically if you use createRoom/joinRoom
|
|
283
|
+
*/
|
|
284
|
+
trackSessionStart(): Promise<void>;
|
|
285
|
+
/**
|
|
286
|
+
* Track a session end (call on game close)
|
|
287
|
+
*/
|
|
288
|
+
trackSessionEnd(): Promise<void>;
|
|
289
|
+
/**
|
|
290
|
+
* Convenience getter for stats (same as getStats but as property style)
|
|
291
|
+
* Note: This returns a promise, use `await wt.stats` or `wt.getStats()`
|
|
292
|
+
*/
|
|
293
|
+
get stats(): Promise<GameStats>;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export { type GameState, type GameStats, type PlayerInfo, type PlayerState, type PlayerStats, type PlayersState, Room, type RoomEventMap, type RoomInfo, type SaveData, Watchtower, type WatchtowerConfig, Watchtower as default };
|