@lzpenguin/server 1.0.6 → 1.0.8
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 +20 -190
- package/index.d.ts +5 -4
- package/index.js +24 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ const server = new RiffleServer({
|
|
|
22
22
|
// 监听数据更新
|
|
23
23
|
server.onData((data) => {
|
|
24
24
|
console.log('World:', data.world);
|
|
25
|
-
console.log('Self:', data.self);
|
|
25
|
+
console.log('Self:', data.self); // self 现在直接是公开数据,与 players[i] 结构一致
|
|
26
26
|
console.log('Players:', data.players);
|
|
27
27
|
});
|
|
28
28
|
|
|
@@ -31,86 +31,11 @@ server.init({
|
|
|
31
31
|
hash: 'game-version-hash-123',
|
|
32
32
|
world: { score: 0, level: 1 },
|
|
33
33
|
self: {
|
|
34
|
-
public: { name: 'Player1' }
|
|
35
|
-
private: { health: 100 }
|
|
34
|
+
public: { name: 'Player1', x: 100, y: 100 }
|
|
36
35
|
}
|
|
37
36
|
});
|
|
38
37
|
```
|
|
39
38
|
|
|
40
|
-
## 新特性 🎉
|
|
41
|
-
|
|
42
|
-
### 同步优化(v2.0+)
|
|
43
|
-
|
|
44
|
-
我们对同步机制进行了全面优化,提供更可靠和高效的数据同步:
|
|
45
|
-
|
|
46
|
-
#### 1. **时间戳和序列号**
|
|
47
|
-
- ✅ 每条消息都包含服务器时间戳和序列号
|
|
48
|
-
- ✅ 自动检测消息丢失和延迟警告
|
|
49
|
-
- ✅ 确保数据按正确顺序处理
|
|
50
|
-
- ✅ 实时显示网络延迟和序列号状态
|
|
51
|
-
|
|
52
|
-
```javascript
|
|
53
|
-
server.onData((data) => {
|
|
54
|
-
console.log('时间戳:', data.timestamp);
|
|
55
|
-
console.log('序列号:', data.sequence);
|
|
56
|
-
console.log('数据:', data.world);
|
|
57
|
-
});
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
#### 2. **操作确认机制**
|
|
61
|
-
- 支持回调确认每个更新操作
|
|
62
|
-
- 自动超时检测(5秒)
|
|
63
|
-
- 返回唯一操作ID用于追踪
|
|
64
|
-
|
|
65
|
-
```javascript
|
|
66
|
-
server.update({
|
|
67
|
-
world: { score: 100 }
|
|
68
|
-
}, (error, result) => {
|
|
69
|
-
if (error) {
|
|
70
|
-
console.error('更新失败:', error);
|
|
71
|
-
} else {
|
|
72
|
-
console.log('更新已确认:', result);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
#### 3. **增量更新**
|
|
78
|
-
- 只发送变化的数据,节省带宽
|
|
79
|
-
- 支持数组追加操作
|
|
80
|
-
- 特别适合笔画等增量数据
|
|
81
|
-
|
|
82
|
-
```javascript
|
|
83
|
-
server.updateDelta({
|
|
84
|
-
world: {
|
|
85
|
-
strokes: {
|
|
86
|
-
$append: [newStroke] // 只追加新笔画
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}, (error, result) => {
|
|
90
|
-
console.log('增量更新完成');
|
|
91
|
-
});
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
#### 4. **自适应推送频率**
|
|
95
|
-
- 服务器根据活动情况动态调整推送频率
|
|
96
|
-
- 高活动:50ms 推送一次
|
|
97
|
-
- 中活动:100ms 推送一次
|
|
98
|
-
- 低活动:200ms 推送一次
|
|
99
|
-
- 无活动:500ms 推送一次
|
|
100
|
-
|
|
101
|
-
#### 5. **统计信息**
|
|
102
|
-
- 实时查看连接状态和同步统计
|
|
103
|
-
- 监控消息丢失和延迟
|
|
104
|
-
|
|
105
|
-
```javascript
|
|
106
|
-
const stats = server.getStats();
|
|
107
|
-
console.log('连接状态:', stats.connected);
|
|
108
|
-
console.log('本地序列号:', stats.localSequence);
|
|
109
|
-
console.log('最后接收序列号:', stats.lastReceivedSequence);
|
|
110
|
-
console.log('待确认操作数:', stats.pendingOperations);
|
|
111
|
-
console.log('更新次数:', stats.updateCount);
|
|
112
|
-
```
|
|
113
|
-
|
|
114
39
|
## API
|
|
115
40
|
|
|
116
41
|
### 构造函数
|
|
@@ -136,8 +61,7 @@ server.init({
|
|
|
136
61
|
hash: 'game-version-hash-123', // 必需:游戏版本哈希值
|
|
137
62
|
world: { score: 0, level: 1 }, // 可选:世界初始数据
|
|
138
63
|
self: {
|
|
139
|
-
public: { name: 'Player1' }
|
|
140
|
-
private: { health: 100 } // 可选:私有数据
|
|
64
|
+
public: { name: 'Player1', x: 100, y: 100 } // 可选:公开数据
|
|
141
65
|
}
|
|
142
66
|
});
|
|
143
67
|
```
|
|
@@ -146,16 +70,15 @@ server.init({
|
|
|
146
70
|
```json
|
|
147
71
|
{
|
|
148
72
|
"world": { "score": 0, "level": 1 },
|
|
149
|
-
"self": {
|
|
150
|
-
"public": { "name": "Player1" },
|
|
151
|
-
"private": { "health": 100 }
|
|
152
|
-
},
|
|
73
|
+
"self": { "name": "Player1", "x": 100, "y": 100 },
|
|
153
74
|
"players": [
|
|
154
|
-
{ "name": "Player2", "
|
|
75
|
+
{ "name": "Player2", "x": 10, "y": 20 }
|
|
155
76
|
]
|
|
156
77
|
}
|
|
157
78
|
```
|
|
158
79
|
|
|
80
|
+
**注意:** `self` 和 `players[i]` 现在具有相同的结构,都只包含公开数据。
|
|
81
|
+
|
|
159
82
|
**关于 hash:**
|
|
160
83
|
- **hash 相同**:传入的值不生效,直接返回现有数据
|
|
161
84
|
- **hash 不同**:删除旧服务器并重新创建,使用传入的值初始化数据
|
|
@@ -168,50 +91,29 @@ server.init({
|
|
|
168
91
|
部分更新数据(合并到现有数据,不替换)。
|
|
169
92
|
|
|
170
93
|
```javascript
|
|
171
|
-
//
|
|
94
|
+
// 更新世界数据
|
|
172
95
|
server.update({ world: { score: 200 } });
|
|
173
96
|
|
|
174
|
-
// 带确认回调的更新(推荐)
|
|
175
|
-
server.update({
|
|
176
|
-
world: { score: 200 }
|
|
177
|
-
}, (error, result) => {
|
|
178
|
-
if (error) {
|
|
179
|
-
console.error('更新失败:', error);
|
|
180
|
-
} else {
|
|
181
|
-
console.log('更新已确认');
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
|
|
185
97
|
// 更新玩家数据
|
|
186
98
|
server.update({
|
|
187
99
|
self: {
|
|
188
|
-
public: {
|
|
189
|
-
private: { health: 90 }
|
|
100
|
+
public: { x: 15, y: 25, rotation: 90 }
|
|
190
101
|
}
|
|
191
102
|
});
|
|
192
103
|
|
|
193
104
|
// 同时更新世界和玩家数据
|
|
194
105
|
server.update({
|
|
195
106
|
world: { score: 200 },
|
|
196
|
-
self: { public: {
|
|
107
|
+
self: { public: { x: 15, y: 25 } }
|
|
197
108
|
});
|
|
198
109
|
```
|
|
199
110
|
|
|
200
|
-
**参数:**
|
|
201
|
-
- `updateData`: 要更新的数据对象
|
|
202
|
-
- `callback` (可选): 确认回调函数 `(error, result) => {}`
|
|
203
|
-
|
|
204
|
-
**返回值:** 操作ID(字符串),可用于追踪操作
|
|
205
|
-
|
|
206
111
|
**响应数据(通过 onData 接收):**
|
|
207
112
|
```json
|
|
208
113
|
{
|
|
209
114
|
"world": { "score": 200, "level": 1 },
|
|
210
|
-
"self": {
|
|
211
|
-
|
|
212
|
-
"private": { "health": 90, "mana": 50 }
|
|
213
|
-
},
|
|
214
|
-
"players": [{ "name": "Player2", "position": { "x": 10, "y": 20 } }]
|
|
115
|
+
"self": { "name": "Player1", "x": 15, "y": 25, "rotation": 90 },
|
|
116
|
+
"players": [{ "name": "Player2", "x": 10, "y": 20 }]
|
|
215
117
|
}
|
|
216
118
|
```
|
|
217
119
|
|
|
@@ -235,87 +137,16 @@ unsubscribe();
|
|
|
235
137
|
**数据格式:**
|
|
236
138
|
```json
|
|
237
139
|
{
|
|
238
|
-
"timestamp": 1705123456789,
|
|
239
|
-
"sequence": 42,
|
|
240
140
|
"world": { "score": 200, "level": 1 },
|
|
241
|
-
"self": {
|
|
242
|
-
"public": { "name": "Player1", "position": { "x": 15, y: 25 } },
|
|
243
|
-
"private": { "health": 90, "mana": 50 }
|
|
244
|
-
},
|
|
141
|
+
"self": { "name": "Player1", "x": 15, "y": 25 },
|
|
245
142
|
"players": [
|
|
246
|
-
{ "name": "Player2", "
|
|
247
|
-
{ "name": "Player3", "
|
|
143
|
+
{ "name": "Player2", "x": 10, "y": 20 },
|
|
144
|
+
{ "name": "Player3", "x": 30, "y": 40 }
|
|
248
145
|
]
|
|
249
146
|
}
|
|
250
147
|
```
|
|
251
148
|
|
|
252
|
-
|
|
253
|
-
- `timestamp`: 服务器时间戳(毫秒)
|
|
254
|
-
- `sequence`: 消息序列号(用于检测消息丢失)
|
|
255
|
-
- `world`: 世界数据
|
|
256
|
-
- `self`: 当前玩家数据
|
|
257
|
-
- `players`: 其他玩家数据
|
|
258
|
-
|
|
259
|
-
### 4. updateDelta() - 增量更新 ✨
|
|
260
|
-
|
|
261
|
-
只发送变化的数据,适合频繁更新的场景(如画板笔画)。
|
|
262
|
-
|
|
263
|
-
```javascript
|
|
264
|
-
// 追加新笔画(不发送整个数组)
|
|
265
|
-
const newStroke = {
|
|
266
|
-
id: 'stroke_123',
|
|
267
|
-
points: [{ x: 0, y: 0 }, { x: 10, y: 10 }],
|
|
268
|
-
color: '#FF0000',
|
|
269
|
-
width: 2
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
server.updateDelta({
|
|
273
|
-
world: {
|
|
274
|
-
strokes: {
|
|
275
|
-
$append: [newStroke] // 使用 $append 标记表示追加
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}, (error, result) => {
|
|
279
|
-
if (!error) {
|
|
280
|
-
console.log('笔画已添加');
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
**参数:**
|
|
286
|
-
- `delta`: 增量数据对象(支持 `$append` 等特殊操作)
|
|
287
|
-
- `callback` (可选): 确认回调函数
|
|
288
|
-
|
|
289
|
-
**返回值:** 操作ID
|
|
290
|
-
|
|
291
|
-
### 5. getStats() - 获取统计信息 ✨
|
|
292
|
-
|
|
293
|
-
获取实时同步统计信息,用于监控和调试。
|
|
294
|
-
|
|
295
|
-
```javascript
|
|
296
|
-
const stats = server.getStats();
|
|
297
|
-
console.log(stats);
|
|
298
|
-
// {
|
|
299
|
-
// connected: true,
|
|
300
|
-
// initialized: true,
|
|
301
|
-
// localSequence: 10,
|
|
302
|
-
// lastReceivedSequence: 8,
|
|
303
|
-
// lastReceivedTimestamp: 1705123456789,
|
|
304
|
-
// pendingOperations: 2,
|
|
305
|
-
// updateCount: 10,
|
|
306
|
-
// lastUpdateTime: 1705123456789
|
|
307
|
-
// }
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
**返回值:**
|
|
311
|
-
- `connected`: 是否已连接
|
|
312
|
-
- `initialized`: 是否已初始化
|
|
313
|
-
- `localSequence`: 本地发送的序列号
|
|
314
|
-
- `lastReceivedSequence`: 最后接收的序列号
|
|
315
|
-
- `lastReceivedTimestamp`: 最后接收的时间戳
|
|
316
|
-
- `pendingOperations`: 待确认的操作数量
|
|
317
|
-
- `updateCount`: 总更新次数
|
|
318
|
-
- `lastUpdateTime`: 最后更新时间
|
|
149
|
+
**注意:** `self` 和 `players[i]` 具有相同的数据结构,都只包含公开数据。
|
|
319
150
|
|
|
320
151
|
**推送时机:** init 响应、定时推送(每 0.2 秒)、init/update 后立即推送
|
|
321
152
|
|
|
@@ -334,8 +165,8 @@ const server = new RiffleServer({
|
|
|
334
165
|
|
|
335
166
|
// 监听服务器推送,更新游戏状态
|
|
336
167
|
server.onData((data) => {
|
|
337
|
-
//
|
|
338
|
-
const myPosition = data.self
|
|
168
|
+
// 更新自己的位置(self 现在直接是公开数据对象)
|
|
169
|
+
const myPosition = { x: data.self.x, y: data.self.y };
|
|
339
170
|
|
|
340
171
|
// 更新其他玩家列表
|
|
341
172
|
const otherPlayers = data.players || [];
|
|
@@ -354,8 +185,7 @@ server.init({
|
|
|
354
185
|
hash: 'v1.0.0',
|
|
355
186
|
world: { score: 0, level: 1 },
|
|
356
187
|
self: {
|
|
357
|
-
public: { name: 'Player1',
|
|
358
|
-
private: { health: 100 }
|
|
188
|
+
public: { name: 'Player1', x: 0, y: 0 }
|
|
359
189
|
}
|
|
360
190
|
});
|
|
361
191
|
|
|
@@ -363,7 +193,7 @@ server.init({
|
|
|
363
193
|
function movePlayer(x, y) {
|
|
364
194
|
server.update({
|
|
365
195
|
self: {
|
|
366
|
-
public: {
|
|
196
|
+
public: { x, y }
|
|
367
197
|
}
|
|
368
198
|
});
|
|
369
199
|
}
|
package/index.d.ts
CHANGED
|
@@ -19,12 +19,13 @@ export interface RiffleServerOptions {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* 玩家数据(用于发送 init/update 请求)
|
|
23
|
+
* 注意:此格式仅用于客户端发送数据,服务端返回的 self 已不再使用此结构
|
|
23
24
|
*/
|
|
24
25
|
export interface PlayerSelfData {
|
|
25
26
|
/** 公开数据 */
|
|
26
27
|
public?: Record<string, any>;
|
|
27
|
-
/**
|
|
28
|
+
/** 私有数据(已弃用,服务端不再使用) */
|
|
28
29
|
private?: Record<string, any>;
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -56,8 +57,8 @@ export interface UpdateData {
|
|
|
56
57
|
export interface ServerData {
|
|
57
58
|
/** 世界数据 */
|
|
58
59
|
world: Record<string, any>;
|
|
59
|
-
/**
|
|
60
|
-
self:
|
|
60
|
+
/** 当前玩家数据(仅公开数据,与 players[i] 结构一致) */
|
|
61
|
+
self: Record<string, any>;
|
|
61
62
|
/** 其他玩家列表(仅公开数据) */
|
|
62
63
|
players: Array<Record<string, any>>;
|
|
63
64
|
}
|
package/index.js
CHANGED
|
@@ -44,7 +44,7 @@ if (typeof window !== 'undefined' && window.WebSocket) {
|
|
|
44
44
|
* // 监听服务器推送的最新数据
|
|
45
45
|
* server.onData((data) => {
|
|
46
46
|
* console.log('World:', data.world);
|
|
47
|
-
* console.log('Self:', data.self);
|
|
47
|
+
* console.log('Self:', data.self); // self 现在直接是公开数据,与 players[i] 结构一致
|
|
48
48
|
* console.log('Players:', data.players);
|
|
49
49
|
* });
|
|
50
50
|
*
|
|
@@ -53,8 +53,7 @@ if (typeof window !== 'undefined' && window.WebSocket) {
|
|
|
53
53
|
* hash: 'game-version-hash-123', // 游戏版本哈希值
|
|
54
54
|
* world: { score: 0, level: 1 }, // 世界初始数据(可选)
|
|
55
55
|
* self: {
|
|
56
|
-
* public: { name: 'Player1' }
|
|
57
|
-
* private: { health: 100 } // 私有数据(可选)
|
|
56
|
+
* public: { name: 'Player1', x: 100, y: 100 } // 公开数据(可选)
|
|
58
57
|
* }
|
|
59
58
|
* });
|
|
60
59
|
*
|
|
@@ -62,8 +61,7 @@ if (typeof window !== 'undefined' && window.WebSocket) {
|
|
|
62
61
|
* server.update({
|
|
63
62
|
* world: { score: 200 },
|
|
64
63
|
* self: {
|
|
65
|
-
* public: {
|
|
66
|
-
* private: { health: 90 }
|
|
64
|
+
* public: { x: 15, y: 25, rotation: 90 }
|
|
67
65
|
* }
|
|
68
66
|
* });
|
|
69
67
|
* ```
|
|
@@ -269,15 +267,25 @@ export class RiffleServer {
|
|
|
269
267
|
* @param {string} initData.hash - 游戏版本哈希值(必需)
|
|
270
268
|
* @param {Object} [initData.world] - 世界初始数据(可选)
|
|
271
269
|
* @param {Object} [initData.self] - 玩家初始数据(可选)
|
|
272
|
-
* @param {Object} [initData.self.public] -
|
|
273
|
-
* @param {Object} [initData.self.private] -
|
|
270
|
+
* @param {Object} [initData.self.public] - 公开数据(可选,旧格式)
|
|
271
|
+
* @param {Object} [initData.self.private] - 私有数据(已弃用)
|
|
274
272
|
* @example
|
|
273
|
+
* // 推荐:直接传递公开数据
|
|
275
274
|
* server.init({
|
|
276
275
|
* hash: 'game-version-hash-123',
|
|
277
276
|
* world: { score: 0, level: 1 },
|
|
278
|
-
* self: {
|
|
279
|
-
* public: { name: 'Player1' }
|
|
280
|
-
*
|
|
277
|
+
* self: {
|
|
278
|
+
* public: { name: 'Player1', x: 100, y: 100 }
|
|
279
|
+
* }
|
|
280
|
+
* });
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* // 也支持直接传递对象(服务端会将其作为 public 数据处理)
|
|
284
|
+
* server.init({
|
|
285
|
+
* hash: 'game-version-hash-123',
|
|
286
|
+
* world: { score: 0 },
|
|
287
|
+
* self: {
|
|
288
|
+
* public: { name: 'Player1' }
|
|
281
289
|
* }
|
|
282
290
|
* });
|
|
283
291
|
*/
|
|
@@ -322,13 +330,12 @@ export class RiffleServer {
|
|
|
322
330
|
* @param {Object} [updateData.world] - 世界数据(可选)
|
|
323
331
|
* @param {Object} [updateData.self] - 玩家数据(可选)
|
|
324
332
|
* @param {Object} [updateData.self.public] - 公开数据(可选)
|
|
325
|
-
* @param {Object} [updateData.self.private] -
|
|
333
|
+
* @param {Object} [updateData.self.private] - 私有数据(已弃用)
|
|
326
334
|
* @example
|
|
327
335
|
* server.update({
|
|
328
336
|
* world: { score: 200 },
|
|
329
337
|
* self: {
|
|
330
|
-
* public: {
|
|
331
|
-
* private: { health: 90 }
|
|
338
|
+
* public: { x: 15, y: 25, rotation: 90 }
|
|
332
339
|
* }
|
|
333
340
|
* });
|
|
334
341
|
*/
|
|
@@ -343,8 +350,11 @@ export class RiffleServer {
|
|
|
343
350
|
* @example
|
|
344
351
|
* const unsubscribe = server.onData((data) => {
|
|
345
352
|
* console.log('World:', data.world);
|
|
346
|
-
* console.log('Self:', data.self);
|
|
353
|
+
* console.log('Self:', data.self); // self 现在直接是公开数据对象,与 players[i] 结构一致
|
|
347
354
|
* console.log('Players:', data.players);
|
|
355
|
+
*
|
|
356
|
+
* // 例如:data.self = { id: 'abc123', name: 'Player1', x: 100, y: 200 }
|
|
357
|
+
* // 而 data.players[0] 也是相同结构:{ id: 'def456', name: 'Player2', x: 150, y: 250 }
|
|
348
358
|
* });
|
|
349
359
|
*
|
|
350
360
|
* // 取消监听
|