@shaunlwm/pickle.ts 0.0.1 → 0.1.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 CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  Type-safe client library for Club Penguin Private Servers.
4
4
 
5
- Built with TypeScript, Socket.IO, and msgpack. Supports multiple CPPS backends through an adapter pattern — currently ships with CPJourney support.
5
+ Built with TypeScript, Socket.IO, and msgpack. Supports multiple CPPS backends through an adapter pattern — ships with CPJourney and CPLegacy support.
6
6
 
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- npm install pickle.ts
10
+ npm install @shaunlwm/pickle.ts
11
11
  ```
12
12
 
13
13
  ## Quick Start
@@ -44,7 +44,7 @@ Creates a new client instance.
44
44
 
45
45
  | Param | Type | Description |
46
46
  |---|---|---|
47
- | `server` | `"CPJourney"` | CPPS server identifier (strongly typed) |
47
+ | `server` | `"CPJourney" \| "CPLegacy"` | CPPS server identifier (strongly typed) |
48
48
  | `options.debug` | `boolean \| LogFn` | Enable debug logging. Pass `true` for console.log, or a custom function |
49
49
 
50
50
  ```typescript
@@ -102,6 +102,7 @@ After `connect()` resolves, `client.player`, `client.room`, and `client.users` a
102
102
  | `client.player` | `PlayerData \| null` | Your penguin's full data (coins, inventory, settings, etc.) |
103
103
  | `client.room` | `number \| null` | Current room ID |
104
104
  | `client.users` | `Map<number, RoomUser>` | All penguins in the current room, keyed by ID |
105
+ | `client.connected` | `boolean` | Whether the client is connected to a game server |
105
106
 
106
107
  `client.users` is automatically kept in sync — players are added/removed as they join/leave, positions and equipment update in real-time.
107
108
 
@@ -134,10 +135,20 @@ client.addItem(itemId)
134
135
  // Social
135
136
  client.buddyRequest(userId)
136
137
  client.buddyAccept(userId)
138
+ client.buddyReject(userId)
139
+ client.buddyRequestSeen(userId)
140
+ client.getBuddy(userId, "buddies") // fetch buddy details
141
+ client.getBuddy(userId, "buddyRequests") // fetch request details
137
142
  client.removeBuddy(userId)
138
143
  client.addIgnore(userId)
139
144
  client.removeIgnore(userId)
140
145
  client.getPlayer(userId)
146
+ client.sendPostcard(userId, "62") // send mail (costs coins)
147
+
148
+ // Stamps & Igloos
149
+ client.getStamps(userId) // view stampbook
150
+ client.getIglooOpen(userId) // check if igloo is open
151
+ client.joinIgloo(userId) // enter igloo
141
152
 
142
153
  // Animation
143
154
  client.sendFrame(frameId)
@@ -191,11 +202,29 @@ client.on("wait_queue_update", ({ position, queueLength }) => {
191
202
  console.log(`Queue: #${position}/${queueLength}`)
192
203
  })
193
204
 
205
+ client.on("kick", ({ reason }) => {
206
+ console.log(`Kicked: ${reason}`)
207
+ })
208
+
209
+ client.on("close_with_error", ({ error }) => {
210
+ console.log(`Disconnected: ${error}`)
211
+ })
212
+
213
+ client.on("buddy_accept", ({ id, username, online }) => {
214
+ console.log(`${username} accepted your buddy request (online: ${online})`)
215
+ })
216
+
217
+ client.on("stamps_result", ({ stamps, username }) => {
218
+ console.log(`${username} has ${stamps.length} stamps`)
219
+ })
220
+
194
221
  client.on("disconnect", () => {
195
222
  console.log("Connection lost")
196
223
  })
197
224
  ```
198
225
 
226
+ The `kick` event fires on CPLegacy, `close_with_error` on CPJourney — both indicate the server forcibly disconnected the client (e.g. duplicate login). State is automatically cleaned up in both cases.
227
+
199
228
  ## Types
200
229
 
201
230
  All types are exported for external use:
package/dist/index.d.ts CHANGED
@@ -1,7 +1,491 @@
1
- export { Client, type ClientOptions, type LogFn } from "./client.js";
2
- export { BaseAdapter, type ConnectOptions } from "./adapters/base-adapter.js";
3
- export type { AdapterName } from "./adapters/index.js";
4
- export type { PlayerData, RoomUser, Buddy, PlayerAppearance, PlayerSettings, Mascot } from "./types/player-types.js";
5
- export type { ClientMessages, ServerMessages } from "./types/message-types.js";
6
- export type { LoginOptions, TokenLoginOptions, ServerInfo, LoginResult, QueueUpdate } from "./types/adapter-types.js";
7
- //# sourceMappingURL=index.d.ts.map
1
+ import { EventEmitter } from 'node:events';
2
+ import { Socket } from 'socket.io-client';
3
+
4
+ type ServerInfo = {
5
+ name: string;
6
+ population: number;
7
+ };
8
+ type LoginOptions = {
9
+ username: string;
10
+ password: string;
11
+ secret?: string;
12
+ };
13
+ type TokenLoginOptions = {
14
+ username: string;
15
+ token: string;
16
+ secret?: string;
17
+ };
18
+ type LoginResult = {
19
+ servers: ServerInfo[];
20
+ key: string;
21
+ username: string;
22
+ moderator: boolean;
23
+ buddyWorlds: string[];
24
+ };
25
+ type QueueUpdate = {
26
+ userId: number;
27
+ position: number;
28
+ queueLength: number;
29
+ };
30
+
31
+ type ConnectOptions = {
32
+ onQueueUpdate?: (update: QueueUpdate) => void;
33
+ };
34
+ declare abstract class BaseAdapter {
35
+ abstract readonly id: string;
36
+ protected socket: Socket | null;
37
+ abstract login(options: LoginOptions | TokenLoginOptions): Promise<LoginResult>;
38
+ abstract connect(serverName: string, loginResult: LoginResult, options?: ConnectOptions): Promise<Socket>;
39
+ abstract disconnect(): void;
40
+ abstract send(action: string, args: Record<string, unknown>): void;
41
+ sendMessage(message: string): void;
42
+ sendEmote(emote: number): void;
43
+ sendSafe(safe: number): void;
44
+ walk(x: number, y: number): void;
45
+ sendFrame(frame: number, set?: boolean): void;
46
+ snowball(x: number, y: number): void;
47
+ joinRoom(room: number, x?: number, y?: number): void;
48
+ addItem(item: number): void;
49
+ equipColor(item: number): void;
50
+ equipHead(item: number): void;
51
+ equipFace(item: number): void;
52
+ equipNeck(item: number): void;
53
+ equipBody(item: number): void;
54
+ equipHand(item: number): void;
55
+ equipFeet(item: number): void;
56
+ equipFlag(item: number): void;
57
+ equipPhoto(item: number): void;
58
+ buddyRequest(id: number): void;
59
+ buddyAccept(id: number): void;
60
+ buddyReject(id: number): void;
61
+ buddyRequestSeen(id: number): void;
62
+ getBuddy(id: number, type: "buddies" | "buddyRequests"): void;
63
+ removeBuddy(id: number): void;
64
+ addIgnore(id: number): void;
65
+ removeIgnore(id: number): void;
66
+ getPlayer(id: number): void;
67
+ getAllSlots(): void;
68
+ getMascots(): void;
69
+ sendPostcard(userId: number, cardId: string): void;
70
+ getStamps(userId: number): void;
71
+ getIglooOpen(igloo: number): void;
72
+ joinIgloo(igloo: number, x?: number, y?: number): void;
73
+ }
74
+
75
+ declare class CpjourneyAdapter extends BaseAdapter {
76
+ readonly id = "CPJourney";
77
+ login(options: LoginOptions | TokenLoginOptions): Promise<LoginResult>;
78
+ connect(serverName: string, loginResult: LoginResult, options?: ConnectOptions): Promise<Socket>;
79
+ send(action: string, args: Record<string, unknown>): void;
80
+ disconnect(): void;
81
+ private createSocket;
82
+ }
83
+
84
+ declare class CplegacyAdapter extends BaseAdapter {
85
+ readonly id = "CPLegacy";
86
+ private version;
87
+ login(options: LoginOptions | TokenLoginOptions): Promise<LoginResult>;
88
+ connect(serverName: string, loginResult: LoginResult, options?: ConnectOptions): Promise<Socket>;
89
+ send(action: string, args: Record<string, unknown>): void;
90
+ disconnect(): void;
91
+ private postLogin;
92
+ }
93
+
94
+ declare const ADAPTERS: {
95
+ readonly CPJourney: typeof CpjourneyAdapter;
96
+ readonly CPLegacy: typeof CplegacyAdapter;
97
+ };
98
+ type AdapterName = keyof typeof ADAPTERS;
99
+
100
+ type PlayerAppearance = {
101
+ color: number;
102
+ hat: number;
103
+ head: number;
104
+ face: number;
105
+ face_mask: number;
106
+ neck: number;
107
+ neck_scarf: number;
108
+ body: number;
109
+ body_shirt: number;
110
+ hand: number;
111
+ hand_glove: number;
112
+ feet: number;
113
+ flag: number;
114
+ photo: number;
115
+ transform: number;
116
+ };
117
+ type PlayerSettings = {
118
+ music_volume: number;
119
+ sfx_volume: number;
120
+ hide_penguins: boolean;
121
+ hide_chats: boolean;
122
+ hide_emotes: boolean;
123
+ hide_ui: boolean;
124
+ hide_usernames: boolean;
125
+ quality: number;
126
+ silence_notifications: boolean;
127
+ opt_out_postcards: boolean;
128
+ opt_out_buddy_requests: boolean;
129
+ };
130
+ type Buddy = {
131
+ id: number;
132
+ username: string;
133
+ online: boolean;
134
+ favorite: boolean;
135
+ items: number[];
136
+ onlineServer: string;
137
+ };
138
+ type RoomUser = PlayerAppearance & {
139
+ id: number;
140
+ username: string;
141
+ displayName: string;
142
+ joinTime: string;
143
+ x: number;
144
+ y: number;
145
+ frame: number;
146
+ walking: number;
147
+ walkingPuffleType: number;
148
+ openSprite?: string;
149
+ mascotGiveaway?: unknown;
150
+ iglooOpen: number;
151
+ iglooBounds: number;
152
+ igloo_slot: number;
153
+ currentLayer: number;
154
+ fireRank: number;
155
+ };
156
+ type PlayerData = RoomUser & {
157
+ coins: number;
158
+ partyCoins: number;
159
+ gems: number;
160
+ rank: number;
161
+ streamer: boolean;
162
+ username_verified: boolean;
163
+ email_verified: boolean;
164
+ settings: PlayerSettings;
165
+ buddies: Buddy[];
166
+ buddyRequests: number[];
167
+ ignores: number[];
168
+ inventory: number[];
169
+ puffleInventory: unknown[];
170
+ igloos: unknown[];
171
+ furniture: unknown[];
172
+ flooring: unknown[];
173
+ inf_skill_points: number;
174
+ highest_floor_reached: number;
175
+ towerMeters: number;
176
+ towerExperience: number;
177
+ };
178
+ type Mascot = {
179
+ id: number;
180
+ name: string;
181
+ giveaway: number;
182
+ stamp: number;
183
+ };
184
+
185
+ type ClientMessages = {
186
+ send_position: {
187
+ x: number;
188
+ y: number;
189
+ };
190
+ send_frame: {
191
+ frame: number;
192
+ set?: boolean;
193
+ };
194
+ send_message: {
195
+ message: string;
196
+ };
197
+ join_room: {
198
+ room: number;
199
+ x?: number;
200
+ y?: number;
201
+ };
202
+ add_item: {
203
+ item: number;
204
+ };
205
+ send_emote: {
206
+ emote: number;
207
+ };
208
+ send_safe: {
209
+ safe: number;
210
+ };
211
+ snowball: {
212
+ x: number;
213
+ y: number;
214
+ };
215
+ buddy_request: {
216
+ id: number;
217
+ };
218
+ buddy_accept: {
219
+ id: number;
220
+ };
221
+ buddy_reject: {
222
+ id: number;
223
+ };
224
+ buddy_request_seen: {
225
+ id: number;
226
+ };
227
+ get_buddy: {
228
+ id: number;
229
+ type: "buddies" | "buddyRequests";
230
+ };
231
+ remove_buddy: {
232
+ id: number;
233
+ };
234
+ get_player: {
235
+ id: number;
236
+ };
237
+ ignore_add: {
238
+ id: number;
239
+ };
240
+ ignore_remove: {
241
+ id: number;
242
+ };
243
+ send_postcard: {
244
+ userId: number;
245
+ cardId: string;
246
+ };
247
+ get_stamps: {
248
+ userId: number;
249
+ };
250
+ get_igloo_open: {
251
+ igloo: number;
252
+ };
253
+ join_igloo: {
254
+ igloo: number;
255
+ x?: number;
256
+ y?: number;
257
+ };
258
+ update_color: {
259
+ item: number;
260
+ };
261
+ update_head: {
262
+ item: number;
263
+ };
264
+ update_face: {
265
+ item: number;
266
+ };
267
+ update_neck: {
268
+ item: number;
269
+ };
270
+ update_body: {
271
+ item: number;
272
+ };
273
+ update_hand: {
274
+ item: number;
275
+ };
276
+ update_feet: {
277
+ item: number;
278
+ };
279
+ update_flag: {
280
+ item: number;
281
+ };
282
+ update_photo: {
283
+ item: number;
284
+ };
285
+ get_all_slots: Record<string, never>;
286
+ get_mascots: Record<string, never>;
287
+ get_weather: Record<string, never>;
288
+ join_server: Record<string, never>;
289
+ check_puffle_sprite: {
290
+ puffleSprite: unknown;
291
+ };
292
+ };
293
+ type ServerMessages = {
294
+ login: {
295
+ success: boolean;
296
+ username: string;
297
+ key: string;
298
+ populations: Record<string, number>;
299
+ moderator: boolean;
300
+ buddyWorlds: string[];
301
+ };
302
+ game_auth: {
303
+ success: boolean;
304
+ token?: string;
305
+ };
306
+ load_player: {
307
+ user: PlayerData;
308
+ };
309
+ join_room: {
310
+ room: number;
311
+ users: RoomUser[];
312
+ };
313
+ add_player: {
314
+ user: RoomUser;
315
+ };
316
+ remove_player: {
317
+ user: number;
318
+ };
319
+ send_position: {
320
+ id: number;
321
+ x: number;
322
+ y: number;
323
+ };
324
+ send_frame: {
325
+ id: number;
326
+ frame: number;
327
+ set?: boolean;
328
+ };
329
+ send_message: {
330
+ id: number;
331
+ message: string;
332
+ };
333
+ send_emote: {
334
+ id: number;
335
+ emote: number;
336
+ };
337
+ set_weather: {
338
+ type: string;
339
+ intensity: number;
340
+ };
341
+ open_sprite: {
342
+ id: number;
343
+ sprite: string;
344
+ };
345
+ close_sprite: {
346
+ id: number;
347
+ };
348
+ wait_queue_update: QueueUpdate;
349
+ cpj_ping: Record<string, never>;
350
+ get_mascots: {
351
+ mascots: Mascot[];
352
+ };
353
+ get_player: {
354
+ user: RoomUser;
355
+ };
356
+ buddy_request: {
357
+ id: number;
358
+ };
359
+ buddy_accept: {
360
+ id: number;
361
+ username: string;
362
+ requester: boolean;
363
+ online: boolean;
364
+ };
365
+ buddy_reject: {
366
+ id: number;
367
+ };
368
+ buddy_request_seen: {
369
+ id: number;
370
+ };
371
+ get_buddy: {
372
+ id: number;
373
+ penguin: Record<string, unknown>;
374
+ type: "buddies" | "buddyRequests";
375
+ };
376
+ send_postcard: {
377
+ coins: number;
378
+ };
379
+ stamps_result: {
380
+ stamps: number[];
381
+ inventory: number[];
382
+ coverOnly: number[];
383
+ unseen: number[];
384
+ username: string;
385
+ playerColor: number;
386
+ stampbook: {
387
+ userId: number;
388
+ colour: number;
389
+ clasp: number;
390
+ highlight: number;
391
+ pattern: number;
392
+ };
393
+ coverStamps: {
394
+ stampId: number;
395
+ itemId: number;
396
+ x: number;
397
+ y: number;
398
+ rotation: number;
399
+ }[];
400
+ };
401
+ ignore_add: {
402
+ id: number;
403
+ username: string;
404
+ };
405
+ ignore_remove: {
406
+ id: number;
407
+ };
408
+ get_igloo_open: {
409
+ open: boolean;
410
+ };
411
+ update_player: {
412
+ id: number;
413
+ item: number;
414
+ slot: string;
415
+ };
416
+ slot: unknown[];
417
+ error: {
418
+ error: number;
419
+ };
420
+ kick: {
421
+ reason: string;
422
+ };
423
+ close_with_error: {
424
+ error: string;
425
+ };
426
+ disconnect: void;
427
+ };
428
+
429
+ type ServerMessageHandler<K extends keyof ServerMessages> = (args: ServerMessages[K]) => void;
430
+ type LogFn = (message: string, ...args: unknown[]) => void;
431
+ type ClientOptions = {
432
+ debug?: boolean | LogFn;
433
+ };
434
+ declare class Client extends EventEmitter {
435
+ player: PlayerData | null;
436
+ room: number | null;
437
+ users: Map<number, RoomUser>;
438
+ connected: boolean;
439
+ private adapter;
440
+ private loginResult;
441
+ private log;
442
+ constructor(server: AdapterName, options?: ClientOptions);
443
+ login(options: LoginOptions | TokenLoginOptions): Promise<ServerInfo[]>;
444
+ connect(serverName: string, options?: ConnectOptions): Promise<void>;
445
+ on<K extends keyof ServerMessages>(event: K, listener: ServerMessageHandler<K>): this;
446
+ on(event: string, listener: (...args: unknown[]) => void): this;
447
+ off<K extends keyof ServerMessages>(event: K, listener: ServerMessageHandler<K>): this;
448
+ off(event: string, listener: (...args: unknown[]) => void): this;
449
+ once<K extends keyof ServerMessages>(event: K, listener: ServerMessageHandler<K>): this;
450
+ once(event: string, listener: (...args: unknown[]) => void): this;
451
+ emit<K extends keyof ServerMessages>(event: K, args: ServerMessages[K]): boolean;
452
+ emit(event: string, ...args: unknown[]): boolean;
453
+ sendMessage(message: string): void;
454
+ sendEmote(emote: number): void;
455
+ sendSafe(safe: number): void;
456
+ walk(x: number, y: number): void;
457
+ sendFrame(frame: number, set?: boolean): void;
458
+ snowball(x: number, y: number): void;
459
+ joinRoom(room: number, x?: number, y?: number): void;
460
+ addItem(item: number): void;
461
+ equipColor(item: number): void;
462
+ equipHead(item: number): void;
463
+ equipFace(item: number): void;
464
+ equipNeck(item: number): void;
465
+ equipBody(item: number): void;
466
+ equipHand(item: number): void;
467
+ equipFeet(item: number): void;
468
+ equipFlag(item: number): void;
469
+ equipPhoto(item: number): void;
470
+ buddyRequest(id: number): void;
471
+ buddyAccept(id: number): void;
472
+ buddyReject(id: number): void;
473
+ buddyRequestSeen(id: number): void;
474
+ getBuddy(id: number, type: "buddies" | "buddyRequests"): void;
475
+ removeBuddy(id: number): void;
476
+ addIgnore(id: number): void;
477
+ removeIgnore(id: number): void;
478
+ getPlayer(id: number): void;
479
+ getAllSlots(): void;
480
+ getMascots(): void;
481
+ sendPostcard(userId: number, cardId: string): void;
482
+ getStamps(userId: number): void;
483
+ getIglooOpen(igloo: number): void;
484
+ joinIgloo(igloo: number, x?: number, y?: number): void;
485
+ sleep(ms: number): Promise<void>;
486
+ disconnect(): void;
487
+ private cleanup;
488
+ private handleMessage;
489
+ }
490
+
491
+ export { type AdapterName, BaseAdapter, type Buddy, Client, type ClientMessages, type ClientOptions, type ConnectOptions, type LogFn, type LoginOptions, type LoginResult, type Mascot, type PlayerAppearance, type PlayerData, type PlayerSettings, type QueueUpdate, type RoomUser, type ServerInfo, type ServerMessages, type TokenLoginOptions };
package/dist/index.js CHANGED
@@ -1,3 +1,2 @@
1
- export { Client } from "./client.js";
2
- export { BaseAdapter } from "./adapters/base-adapter.js";
1
+ import{EventEmitter as A}from"events";import{io as f}from"socket.io-client";import w from"socket.io-msgpack-parser";var p=class{socket=null;sendMessage(e){this.send("send_message",{message:e})}sendEmote(e){this.send("send_emote",{emote:e})}sendSafe(e){this.send("send_safe",{safe:e})}walk(e,s){this.send("send_position",{x:e,y:s})}sendFrame(e,s){this.send("send_frame",{frame:e,set:s})}snowball(e,s){this.send("snowball",{x:e,y:s})}joinRoom(e,s,o){this.send("join_room",{room:e,x:s??0,y:o??0})}addItem(e){this.send("add_item",{item:e})}equipColor(e){this.send("update_color",{item:e})}equipHead(e){this.send("update_head",{item:e})}equipFace(e){this.send("update_face",{item:e})}equipNeck(e){this.send("update_neck",{item:e})}equipBody(e){this.send("update_body",{item:e})}equipHand(e){this.send("update_hand",{item:e})}equipFeet(e){this.send("update_feet",{item:e})}equipFlag(e){this.send("update_flag",{item:e})}equipPhoto(e){this.send("update_photo",{item:e})}buddyRequest(e){this.send("buddy_request",{id:e})}buddyAccept(e){this.send("buddy_accept",{id:e})}buddyReject(e){this.send("buddy_reject",{id:e})}buddyRequestSeen(e){this.send("buddy_request_seen",{id:e})}getBuddy(e,s){this.send("get_buddy",{id:e,type:s})}removeBuddy(e){this.send("remove_buddy",{id:e})}addIgnore(e){this.send("ignore_add",{id:e})}removeIgnore(e){this.send("ignore_remove",{id:e})}getPlayer(e){this.send("get_player",{id:e})}getAllSlots(){this.send("get_all_slots",{})}getMascots(){this.send("get_mascots",{})}sendPostcard(e,s){this.send("send_postcard",{userId:e,cardId:s})}getStamps(e){this.send("get_stamps",{userId:e})}getIglooOpen(e){this.send("get_igloo_open",{igloo:e})}joinIgloo(e,s,o){this.send("join_igloo",{igloo:e,x:s??0,y:o??0})}};var _="wss://play.cpjourney.net",R="skip",l=class extends p{id="CPJourney";async login(e){let s=this.createSocket("/world/login/");return await new Promise((n,t)=>{let a=setTimeout(()=>{s.disconnect(),t(new Error("Login timed out"))},1e4);s.on("connect",()=>{let r=e.secret??R,i="token"in e?{username:e.username,token:e.token,secret:r}:{username:e.username,password:e.password,secret:r};s.emit("message",{action:"login",args:i})}),s.on("message",r=>{if(r.action!=="login")return;clearTimeout(a),s.disconnect();let i=r.args;if(!i.success){t(new Error("Login failed"));return}let u=Object.entries(i.populations).map(([c,m])=>({name:c,population:m}));n({servers:u,key:i.key,username:i.username,moderator:i.moderator,buddyWorlds:i.buddyWorlds??[]})}),s.on("connect_error",r=>{clearTimeout(a),s.disconnect(),t(new Error(`Login connection failed: ${r.message}`))})})}async connect(e,s,o){let n=this.createSocket(`/world/${e.toLowerCase()}/`);return this.socket=n,await new Promise((t,a)=>{let r=!1;n.on("connect",()=>{n.emit("message",{action:"game_auth",args:{username:s.username,key:s.key,createToken:!1,joinInvis:!1,takeoverMascot:!1,token:""}})}),n.on("message",i=>{switch(i.action){case"wait_queue_update":{o?.onQueueUpdate?.(i.args);break}case"game_auth":{if(r)break;if(r=!0,!i.args.success){n.disconnect(),a(new Error("Game auth failed"));return}n.emit("message",{action:"join_server",args:{}}),t();break}}}),n.on("connect_error",i=>{n.disconnect(),a(new Error(`Game connection failed: ${i.message}`))})}),n}send(e,s){if(!this.socket)throw new Error("Not connected");this.socket.emit("message",{action:e,args:s})}disconnect(){this.socket?.disconnect(),this.socket=null}createSocket(e){return f(_,{path:e,parser:w,transports:["polling","websocket"],reconnection:!1})}};import{io as S}from"socket.io-client";import L from"socket.io-msgpack-parser";var P="https://api.cplegacy.com/login",q="https://api.cplegacy.com/token_login";function O(d){let e=d.on.bind(d);return d.on=function(s,o){return s==="message"?e(s,(n,t)=>{o({action:n,args:t??{}})}):e(s,o)},d}var h=class extends p{id="CPLegacy";version="";async login(e){let s="token"in e?q:P,o="token"in e?{username:e.username,token:e.token}:{username:e.username,password:e.password,version:this.version},n=await this.postLogin(s,o);if(!n.success&&n.wantedVersion&&(this.version=n.wantedVersion,o.version=this.version,n=await this.postLogin(s,o)),!n.success)throw new Error(`Login failed: ${n.message??"unknown error"}`);return{servers:Object.entries(n.populations??{}).map(([a,r])=>({name:a,population:r})),key:n.key,username:n.username,moderator:!1,buddyWorlds:[]}}async connect(e,s,o){let n=`wss://${e.toLowerCase()}.server.cplegacy.com`,t=S(n,{path:"/socket.io/",parser:L,transports:["websocket"],reconnection:!1});return this.socket=t,await new Promise((a,r)=>{let i=setTimeout(()=>{t.disconnect(),r(new Error("Game connection timed out"))},15e3);t.on("connect",()=>{t.emit("message","game_auth",{username:s.username,key:s.key,createToken:!1,token:""})}),t.on("message",(u,c)=>{switch(u){case"wait_queue_update":{o?.onQueueUpdate?.(c);break}case"game_auth":{if(!c.success){clearTimeout(i),t.disconnect(),r(new Error("Game auth failed"));return}t.emit("message","load_player",{}),t.emit("message","join_server",{}),clearTimeout(i),a();break}}}),t.on("connect_error",u=>{clearTimeout(i),t.disconnect(),r(new Error(`Game connection failed: ${u.message}`))})}),O(t)}send(e,s){if(!this.socket)throw new Error("Not connected");this.socket.emit("message",e,s)}disconnect(){this.socket?.disconnect(),this.socket=null}async postLogin(e,s){return(await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(s)})).json()}};var M={CPJourney:l,CPLegacy:h};function y(d){return new M[d]}function v(d){return typeof d=="object"&&d!==null&&"action"in d&&"args"in d}var b=class extends A{player=null;room=null;users=new Map;connected=!1;adapter;loginResult=null;log=null;constructor(e,s){super(),this.adapter=y(e),s?.debug&&(this.log=typeof s.debug=="function"?s.debug:console.log.bind(console))}async login(e){return this.log?.("[login] logging in as",e.username),this.loginResult=await this.adapter.login(e),this.log?.("[login] success \u2014",this.loginResult.servers.length,"servers"),this.loginResult.servers}async connect(e,s){if(!this.loginResult)throw new Error("Must call login() first");this.log?.("[connect] joining",e);let o={...s,onQueueUpdate:t=>{this.log?.("[queue]",`#${t.position}/${t.queueLength}`),this.emit("wait_queue_update",t),s?.onQueueUpdate?.(t)}},n=await this.adapter.connect(e,this.loginResult,o);n.on("message",t=>{v(t)&&this.handleMessage(t)}),n.on("disconnect",()=>{this.log?.("[disconnect] connection lost"),this.cleanup(),this.emit("disconnect")}),await new Promise((t,a)=>{let r=setTimeout(()=>{a(new Error("Waiting for load_player timed out"))},1e4),i=!1,u=!1,c=()=>{i&&u&&(clearTimeout(r),this.connected=!0,t())},m=g=>{if(v(g))switch(g.action){case"load_player":i=!0,c();break;case"join_room":u=!0,c();break;case"kick":clearTimeout(r),n.off("message",m),a(new Error(`Kicked: ${g.args.reason??"unknown"}`));break;case"close_with_error":clearTimeout(r),n.off("message",m),a(new Error(`Kicked: ${g.args.error??"unknown"}`));break}},k=()=>{(!i||!u)&&(clearTimeout(r),a(new Error("Disconnected before fully loaded")))};n.on("message",m),n.once("disconnect",k)})}on(e,s){return super.on(e,s)}off(e,s){return super.off(e,s)}once(e,s){return super.once(e,s)}emit(e,...s){return super.emit(e,...s)}sendMessage(e){this.adapter.sendMessage(e)}sendEmote(e){this.adapter.sendEmote(e)}sendSafe(e){this.adapter.sendSafe(e)}walk(e,s){this.adapter.walk(e,s)}sendFrame(e,s){this.adapter.sendFrame(e,s)}snowball(e,s){this.adapter.snowball(e,s)}joinRoom(e,s,o){this.adapter.joinRoom(e,s,o)}addItem(e){this.adapter.addItem(e)}equipColor(e){this.adapter.equipColor(e)}equipHead(e){this.adapter.equipHead(e)}equipFace(e){this.adapter.equipFace(e)}equipNeck(e){this.adapter.equipNeck(e)}equipBody(e){this.adapter.equipBody(e)}equipHand(e){this.adapter.equipHand(e)}equipFeet(e){this.adapter.equipFeet(e)}equipFlag(e){this.adapter.equipFlag(e)}equipPhoto(e){this.adapter.equipPhoto(e)}buddyRequest(e){this.adapter.buddyRequest(e)}buddyAccept(e){this.adapter.buddyAccept(e)}buddyReject(e){this.adapter.buddyReject(e)}buddyRequestSeen(e){this.adapter.buddyRequestSeen(e)}getBuddy(e,s){this.adapter.getBuddy(e,s)}removeBuddy(e){this.adapter.removeBuddy(e)}addIgnore(e){this.adapter.addIgnore(e)}removeIgnore(e){this.adapter.removeIgnore(e)}getPlayer(e){this.adapter.getPlayer(e)}getAllSlots(){this.adapter.getAllSlots()}getMascots(){this.adapter.getMascots()}sendPostcard(e,s){this.adapter.sendPostcard(e,s)}getStamps(e){this.adapter.getStamps(e)}getIglooOpen(e){this.adapter.getIglooOpen(e)}joinIgloo(e,s,o){this.adapter.joinIgloo(e,s,o)}sleep(e){return new Promise(s=>setTimeout(s,e))}disconnect(){this.log?.("[disconnect] closing connection"),this.adapter.disconnect(),this.cleanup()}cleanup(){this.connected=!1,this.player=null,this.room=null,this.users.clear()}handleMessage(e){let{action:s,args:o}=e;switch(s){case"load_player":{let n=o.user;this.player=n,this.log?.("[load_player]",n.username,`id=${n.id}`);break}case"join_room":{let n=o.room,t=o.users;this.room=n,this.users.clear();for(let a of t)this.users.set(a.id,a);this.log?.("[join_room]",`room=${n}`,`users=${t.length}`);break}case"add_player":{let n=o.user;this.users.set(n.id,n),this.log?.("[add_player]",n.username,`id=${n.id}`);break}case"remove_player":{let n=o.user;this.users.delete(n),this.log?.("[remove_player]",`id=${n}`);break}case"send_position":{let n=o.id,t=this.users.get(n);t&&(t.x=o.x,t.y=o.y);break}case"send_frame":{let n=o.id,t=this.users.get(n);t&&(t.frame=o.frame);break}case"update_player":{let n=o.id,t=o.slot,a=o.item,r=this.users.get(n);r&&t in r&&(r[t]=a);break}case"kick":{let n=o.reason??"unknown";this.log?.("[kick]",n),this.cleanup();break}case"close_with_error":{let n=o.error??"unknown";this.log?.("[kick]",n),this.cleanup();break}default:{this.log?.(`[${s}]`,JSON.stringify(o));break}}this.emit(s,o)}};export{p as BaseAdapter,b as Client};
3
2
  //# sourceMappingURL=index.js.map