@lzpenguin/server 1.0.0 → 1.0.2

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 (3) hide show
  1. package/index.d.ts +101 -0
  2. package/index.js +75 -18
  3. package/package.json +5 -3
package/index.d.ts ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Riffle 游戏服务器 WebSocket 客户端 SDK
3
+ */
4
+
5
+ /**
6
+ * RiffleServer 构造函数选项
7
+ */
8
+ export interface RiffleServerOptions {
9
+ /** WebSocket 服务器 URL */
10
+ url: string;
11
+ /** 游戏贴文 ID */
12
+ postId: number;
13
+ /** 认证 token */
14
+ token: string;
15
+ /** 是否自动重连,默认 true */
16
+ autoReconnect?: boolean;
17
+ /** 重连间隔(毫秒),默认 3000 */
18
+ reconnectInterval?: number;
19
+ }
20
+
21
+ /**
22
+ * 玩家数据
23
+ */
24
+ export interface PlayerSelfData {
25
+ /** 公开数据 */
26
+ public?: Record<string, any>;
27
+ /** 私有数据 */
28
+ private?: Record<string, any>;
29
+ }
30
+
31
+ /**
32
+ * 初始化数据
33
+ */
34
+ export interface InitData {
35
+ /** 游戏版本哈希值(必需) */
36
+ hash: string;
37
+ /** 世界初始数据(可选) */
38
+ world?: Record<string, any>;
39
+ /** 玩家初始数据(可选) */
40
+ self?: PlayerSelfData;
41
+ }
42
+
43
+ /**
44
+ * 更新数据
45
+ */
46
+ export interface UpdateData {
47
+ /** 世界数据(可选,部分更新) */
48
+ world?: Record<string, any>;
49
+ /** 玩家数据(可选,部分更新) */
50
+ self?: PlayerSelfData;
51
+ }
52
+
53
+ /**
54
+ * 服务器推送的数据
55
+ */
56
+ export interface ServerData {
57
+ /** 世界数据 */
58
+ world: Record<string, any>;
59
+ /** 当前玩家数据 */
60
+ self: PlayerSelfData;
61
+ /** 其他玩家列表(仅公开数据) */
62
+ players: Array<Record<string, any>>;
63
+ }
64
+
65
+ /**
66
+ * RiffleServer - 游戏服务器 WebSocket 客户端
67
+ */
68
+ export class RiffleServer {
69
+ /**
70
+ * 创建 RiffleServer 实例
71
+ * @param options 配置选项
72
+ */
73
+ constructor(options: RiffleServerOptions);
74
+
75
+ /** 是否已连接 */
76
+ readonly isConnected: boolean;
77
+ /** 是否正在连接 */
78
+ readonly isConnecting: boolean;
79
+ /** 是否已初始化 */
80
+ readonly isInitialized: boolean;
81
+
82
+ /**
83
+ * 初始化服务器(必需,连接后必须先调用)
84
+ * @param initData 初始化数据
85
+ */
86
+ init(initData: InitData): void;
87
+
88
+ /**
89
+ * 更新数据(部分更新,合并到现有数据)
90
+ * @param updateData 更新数据
91
+ */
92
+ update(updateData: UpdateData): void;
93
+
94
+ /**
95
+ * 监听服务器推送的最新数据
96
+ * @param callback 回调函数,接收服务器推送的数据
97
+ * @returns 取消监听的函数
98
+ */
99
+ onData(callback: (data: ServerData) => void): () => void;
100
+ }
101
+
package/index.js CHANGED
@@ -1,4 +1,32 @@
1
- import WebSocket from 'ws';
1
+ // 检测运行环境,浏览器使用原生 WebSocket,Node.js 使用 ws
2
+ let WebSocketImpl;
3
+ let isBrowser = false;
4
+
5
+ if (typeof window !== 'undefined' && window.WebSocket) {
6
+ // 浏览器环境
7
+ WebSocketImpl = window.WebSocket;
8
+ isBrowser = true;
9
+ } else {
10
+ // Node.js 环境 - 使用动态导入
11
+ try {
12
+ // 使用 require 或 import,根据环境选择
13
+ if (typeof require !== 'undefined') {
14
+ // CommonJS 环境
15
+ WebSocketImpl = require('ws');
16
+ } else {
17
+ // ES Module 环境 - 在运行时动态导入
18
+ // 注意:这需要在异步上下文中使用
19
+ WebSocketImpl = null; // 将在 connect 方法中动态导入
20
+ }
21
+ } catch (e) {
22
+ // 如果无法导入 ws,尝试使用全局 WebSocket(某些打包工具可能会提供)
23
+ if (typeof WebSocket !== 'undefined') {
24
+ WebSocketImpl = WebSocket;
25
+ } else {
26
+ throw new Error('WebSocket is not available. In Node.js, please install "ws" package.');
27
+ }
28
+ }
29
+ }
2
30
 
3
31
  /**
4
32
  * RiffleServer - 游戏服务器 WebSocket 客户端
@@ -79,29 +107,43 @@ export class RiffleServer {
79
107
  // 当前服务器数据缓存
80
108
  this.currentData = null;
81
109
 
82
- // 连接状态
83
- this.connect();
110
+ // 连接状态(异步调用,不阻塞构造函数)
111
+ this.connect().catch(err => {
112
+ console.error('[RiffleServer] Initial connection failed:', err);
113
+ });
84
114
  }
85
115
 
86
116
  /**
87
117
  * 连接到 WebSocket 服务器
88
118
  * @private
89
119
  */
90
- connect() {
91
- if (this.isConnecting || (this.isConnected && this.ws?.readyState === WebSocket.OPEN)) {
120
+ async connect() {
121
+ if (this.isConnecting || (this.isConnected && this.ws?.readyState === (WebSocketImpl?.OPEN ?? 1))) {
92
122
  return;
93
123
  }
94
124
 
95
125
  this.isConnecting = true;
96
126
 
127
+ // 如果在 Node.js 环境且 WebSocketImpl 未初始化,动态导入
128
+ if (!isBrowser && !WebSocketImpl) {
129
+ try {
130
+ const wsModule = await import('ws');
131
+ WebSocketImpl = wsModule.default;
132
+ } catch (e) {
133
+ this.isConnecting = false;
134
+ console.error('[RiffleServer] Failed to import ws module:', e);
135
+ throw new Error('WebSocket is not available. In Node.js, please install "ws" package.');
136
+ }
137
+ }
138
+
97
139
  // 构建 WebSocket URL(包含 post_id 和 token)
98
140
  let wsUrl = `${this.url}?post_id=${this.postId}&token=${this.token}`;
99
141
 
100
-
101
142
  try {
102
- this.ws = new WebSocket(wsUrl);
143
+ this.ws = new WebSocketImpl(wsUrl);
103
144
 
104
- this.ws.on('open', () => {
145
+ // 统一事件处理:浏览器用 addEventListener,Node.js 用 .on()
146
+ const handleOpen = () => {
105
147
  this.isConnected = true;
106
148
  this.isConnecting = false;
107
149
  this.isInitialized = false; // 连接后重置初始化状态
@@ -113,11 +155,13 @@ export class RiffleServer {
113
155
  this.pendingInit = null;
114
156
  this._doInit(initData);
115
157
  }
116
- });
158
+ };
117
159
 
118
- this.ws.on('message', (data) => {
160
+ const handleMessage = (event) => {
119
161
  try {
120
- const message = JSON.parse(data.toString());
162
+ // 浏览器: event.data, Node.js: event (Buffer 或 string)
163
+ const data = event.data || event;
164
+ const message = JSON.parse(typeof data === 'string' ? data : data.toString());
121
165
 
122
166
  // 检查是否是错误消息
123
167
  if (message.error) {
@@ -140,13 +184,13 @@ export class RiffleServer {
140
184
  } catch (error) {
141
185
  console.error('[RiffleServer] Failed to parse message:', error);
142
186
  }
143
- });
187
+ };
144
188
 
145
- this.ws.on('error', (error) => {
189
+ const handleError = (error) => {
146
190
  console.error('[RiffleServer] WebSocket error:', error);
147
- });
191
+ };
148
192
 
149
- this.ws.on('close', () => {
193
+ const handleClose = () => {
150
194
  this.isConnected = false;
151
195
  this.isConnecting = false;
152
196
  this.isInitialized = false; // 断开连接后重置初始化状态
@@ -159,7 +203,20 @@ export class RiffleServer {
159
203
  this.connect();
160
204
  }, this.reconnectInterval);
161
205
  }
162
- });
206
+ };
207
+
208
+ // 根据环境使用不同的事件绑定方式
209
+ if (isBrowser) {
210
+ this.ws.addEventListener('open', handleOpen);
211
+ this.ws.addEventListener('message', handleMessage);
212
+ this.ws.addEventListener('error', handleError);
213
+ this.ws.addEventListener('close', handleClose);
214
+ } else {
215
+ this.ws.on('open', handleOpen);
216
+ this.ws.on('message', handleMessage);
217
+ this.ws.on('error', handleError);
218
+ this.ws.on('close', handleClose);
219
+ }
163
220
  } catch (error) {
164
221
  this.isConnecting = false;
165
222
  console.error('[RiffleServer] Connection error:', error);
@@ -189,7 +246,7 @@ export class RiffleServer {
189
246
  * @param {Object} updateData - 更新数据
190
247
  */
191
248
  sendUpdate(updateData) {
192
- if (!this.isConnected || this.ws?.readyState !== WebSocket.OPEN) {
249
+ if (!this.isConnected || this.ws?.readyState !== WebSocketImpl.OPEN) {
193
250
  console.warn('[RiffleServer] Not connected, cannot send update');
194
251
  return;
195
252
  }
@@ -231,7 +288,7 @@ export class RiffleServer {
231
288
  }
232
289
 
233
290
  // 如果还未连接,保存 init 请求,连接建立后自动执行
234
- if (!this.isConnected || this.ws?.readyState !== WebSocket.OPEN) {
291
+ if (!this.isConnected || this.ws?.readyState !== WebSocketImpl.OPEN) {
235
292
  console.log('[RiffleServer] Not connected yet, will init after connection');
236
293
  this.pendingInit = initData;
237
294
  return;
package/package.json CHANGED
@@ -1,18 +1,21 @@
1
1
  {
2
2
  "name": "@lzpenguin/server",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Riffle 游戏服务器 WebSocket 客户端 SDK",
5
5
  "license": "ISC",
6
6
  "author": "lzpenguin",
7
7
  "type": "module",
8
8
  "main": "./index.js",
9
+ "types": "./index.d.ts",
9
10
  "exports": {
10
11
  ".": {
11
- "import": "./index.js"
12
+ "import": "./index.js",
13
+ "types": "./index.d.ts"
12
14
  }
13
15
  },
14
16
  "files": [
15
17
  "index.js",
18
+ "index.d.ts",
16
19
  "README.md"
17
20
  ],
18
21
  "keywords": [
@@ -34,4 +37,3 @@
34
37
  "registry": "https://registry.npmjs.org/"
35
38
  }
36
39
  }
37
-