@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.
- package/index.d.ts +101 -0
- package/index.js +75 -18
- 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
|
-
|
|
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 ===
|
|
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
|
|
143
|
+
this.ws = new WebSocketImpl(wsUrl);
|
|
103
144
|
|
|
104
|
-
|
|
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
|
-
|
|
160
|
+
const handleMessage = (event) => {
|
|
119
161
|
try {
|
|
120
|
-
|
|
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
|
-
|
|
189
|
+
const handleError = (error) => {
|
|
146
190
|
console.error('[RiffleServer] WebSocket error:', error);
|
|
147
|
-
}
|
|
191
|
+
};
|
|
148
192
|
|
|
149
|
-
|
|
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 !==
|
|
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 !==
|
|
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.
|
|
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
|
-
|