@lzpenguin/server 1.0.7 → 1.0.9

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 (4) hide show
  1. package/README.md +94 -66
  2. package/index.d.ts +72 -10
  3. package/index.js +125 -39
  4. 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,8 +31,7 @@ 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
  ```
@@ -51,6 +50,82 @@ new RiffleServer({
51
50
  })
52
51
  ```
53
52
 
53
+ ## API 方法
54
+
55
+ ### 细粒度操作 API(推荐)
56
+
57
+ 从 v1.1.0 起,提供了更细粒度的操作 API,使代码更加语义化和易读。
58
+
59
+ #### World 数据操作
60
+
61
+ ```javascript
62
+ // 添加/设置单个键值对
63
+ server.world.add('score', 100);
64
+ server.world.update('level', 2);
65
+
66
+ // 删除键(设置为 null)
67
+ server.world.delete('oldKey');
68
+
69
+ // 批量设置
70
+ server.world.set({
71
+ score: 100,
72
+ level: 2,
73
+ players: 10
74
+ });
75
+ ```
76
+
77
+ #### Self 数据操作
78
+
79
+ ```javascript
80
+ // 添加/设置单个键值对
81
+ server.self.add('x', 150);
82
+ server.self.update('y', 250);
83
+
84
+ // 删除键(设置为 null)
85
+ server.self.delete('oldProp');
86
+
87
+ // 批量设置
88
+ server.self.set({
89
+ x: 150,
90
+ y: 250,
91
+ rotation: 90
92
+ });
93
+ ```
94
+
95
+ #### 完整示例
96
+
97
+ ```javascript
98
+ import { RiffleServer } from '@lzpenguin/server';
99
+
100
+ const server = new RiffleServer({
101
+ url: 'wss://api.riffle.app',
102
+ postId: 123456,
103
+ token: 'your-token-here'
104
+ });
105
+
106
+ // 监听数据更新
107
+ server.onData((data) => {
108
+ console.log('World:', data.world);
109
+ console.log('Self:', data.self);
110
+ console.log('Players:', data.players);
111
+ });
112
+
113
+ // 初始化
114
+ server.init({
115
+ hash: 'v1.0.0',
116
+ world: { score: 0, level: 1 },
117
+ self: { public: { name: 'Player1', x: 0, y: 0 } }
118
+ });
119
+
120
+ // 使用细粒度 API 更新数据
121
+ server.world.update('score', 100); // 更新分数
122
+ server.self.update('x', 150); // 更新 X 坐标
123
+ server.self.update('y', 250); // 更新 Y 坐标
124
+
125
+ // 批量更新
126
+ server.self.set({ x: 200, y: 300, rotation: 45 });
127
+ ```
128
+
54
129
  ## 三种消息类型
55
130
 
56
131
  ### 1. init() - 初始化服务器
@@ -62,8 +137,7 @@ server.init({
62
137
  hash: 'game-version-hash-123', // 必需:游戏版本哈希值
63
138
  world: { score: 0, level: 1 }, // 可选:世界初始数据
64
139
  self: {
65
- public: { name: 'Player1' }, // 可选:公开数据
66
- private: { health: 100 } // 可选:私有数据
140
+ public: { name: 'Player1', x: 100, y: 100 } // 可选:公开数据
67
141
  }
68
142
  });
69
143
  ```
@@ -72,16 +146,15 @@ server.init({
72
146
  ```json
73
147
  {
74
148
  "world": { "score": 0, "level": 1 },
75
- "self": {
76
- "public": { "name": "Player1" },
77
- "private": { "health": 100 }
78
- },
149
+ "self": { "name": "Player1", "x": 100, "y": 100 },
79
150
  "players": [
80
- { "name": "Player2", "position": { "x": 10, "y": 20 } }
151
+ { "name": "Player2", "x": 10, "y": 20 }
81
152
  ]
82
153
  }
83
154
  ```
84
155
 
156
+ **注意:** `self` 和 `players[i]` 现在具有相同的结构,都只包含公开数据。
157
+
85
158
  **关于 hash:**
86
159
  - **hash 相同**:传入的值不生效,直接返回现有数据
87
160
  - **hash 不同**:删除旧服务器并重新创建,使用传入的值初始化数据
@@ -89,44 +162,7 @@ server.init({
89
162
 
90
163
  **注意:** init 成功后服务器每 0.2 秒自动推送最新数据
91
164
 
92
- ### 2. update() - 更新数据
93
-
94
- 部分更新数据(合并到现有数据,不替换)。
95
-
96
- ```javascript
97
- // 更新世界数据
98
- server.update({ world: { score: 200 } });
99
-
100
- // 更新玩家数据
101
- server.update({
102
- self: {
103
- public: { position: { x: 15, y: 25 } },
104
- private: { health: 90 }
105
- }
106
- });
107
-
108
- // 同时更新世界和玩家数据
109
- server.update({
110
- world: { score: 200 },
111
- self: { public: { position: { x: 15, y: 25 } } }
112
- });
113
- ```
114
-
115
- **响应数据(通过 onData 接收):**
116
- ```json
117
- {
118
- "world": { "score": 200, "level": 1 },
119
- "self": {
120
- "public": { "name": "Player1", "position": { "x": 15, "y": 25 } },
121
- "private": { "health": 90, "mana": 50 }
122
- },
123
- "players": [{ "name": "Player2", "position": { "x": 10, "y": 20 } }]
124
- }
125
- ```
126
-
127
- **注意:** 只更新提供的字段,未提供的字段保持不变,更新后立即推送最新数据
128
-
129
- ### 3. onData() - 监听数据推送
165
+ ### 2. onData() - 监听数据推送
130
166
 
131
167
  监听服务器推送的最新数据。init 成功后每 0.2 秒自动推送,调用 init/update 后立即推送。
132
168
 
@@ -145,17 +181,16 @@ unsubscribe();
145
181
  ```json
146
182
  {
147
183
  "world": { "score": 200, "level": 1 },
148
- "self": {
149
- "public": { "name": "Player1", "position": { "x": 15, "y": 25 } },
150
- "private": { "health": 90, "mana": 50 }
151
- },
184
+ "self": { "name": "Player1", "x": 15, "y": 25 },
152
185
  "players": [
153
- { "name": "Player2", "position": { "x": 10, "y": 20 } },
154
- { "name": "Player3", "position": { "x": 30, "y": 40 } }
186
+ { "name": "Player2", "x": 10, "y": 20 },
187
+ { "name": "Player3", "x": 30, "y": 40 }
155
188
  ]
156
189
  }
157
190
  ```
158
191
 
192
+ **注意:** `self` 和 `players[i]` 具有相同的数据结构,都只包含公开数据。
193
+
159
194
  **推送时机:** init 响应、定时推送(每 0.2 秒)、init/update 后立即推送
160
195
 
161
196
  ## 联机游戏示例
@@ -173,8 +208,8 @@ const server = new RiffleServer({
173
208
 
174
209
  // 监听服务器推送,更新游戏状态
175
210
  server.onData((data) => {
176
- // 更新自己的位置
177
- const myPosition = data.self?.public?.position;
211
+ // 更新自己的位置(self 现在直接是公开数据对象)
212
+ const myPosition = { x: data.self.x, y: data.self.y };
178
213
 
179
214
  // 更新其他玩家列表
180
215
  const otherPlayers = data.players || [];
@@ -193,25 +228,18 @@ server.init({
193
228
  hash: 'v1.0.0',
194
229
  world: { score: 0, level: 1 },
195
230
  self: {
196
- public: { name: 'Player1', position: { x: 0, y: 0 } },
197
- private: { health: 100 }
231
+ public: { name: 'Player1', x: 0, y: 0 }
198
232
  }
199
233
  });
200
234
 
201
235
  // 更新玩家位置
202
236
  function movePlayer(x, y) {
203
- server.update({
204
- self: {
205
- public: { position: { x, y } }
206
- }
207
- });
237
+ server.self.set({ x, y });
208
238
  }
209
239
 
210
240
  // 更新分数
211
241
  function updateScore(score) {
212
- server.update({
213
- world: { score }
214
- });
242
+ server.world.update('score', score);
215
243
  }
216
244
  ```
217
245
 
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,12 +57,74 @@ export interface UpdateData {
56
57
  export interface ServerData {
57
58
  /** 世界数据 */
58
59
  world: Record<string, any>;
59
- /** 当前玩家数据 */
60
- self: PlayerSelfData;
60
+ /** 当前玩家数据(仅公开数据,与 players[i] 结构一致) */
61
+ self: Record<string, any>;
61
62
  /** 其他玩家列表(仅公开数据) */
62
63
  players: Array<Record<string, any>>;
63
64
  }
64
65
 
66
+ /**
67
+ * WorldManager - 管理 world 数据的细粒度操作
68
+ */
69
+ export class WorldManager {
70
+ /**
71
+ * 添加或设置 world 中的键值对
72
+ * @param key 键名
73
+ * @param value 值
74
+ */
75
+ add(key: string, value: any): void;
76
+
77
+ /**
78
+ * 更新 world 中的键值对(与 add 相同,提供语义化接口)
79
+ * @param key 键名
80
+ * @param value 值
81
+ */
82
+ update(key: string, value: any): void;
83
+
84
+ /**
85
+ * 删除 world 中的键(通过设置为 null)
86
+ * @param key 键名
87
+ */
88
+ delete(key: string): void;
89
+
90
+ /**
91
+ * 批量设置多个键值对
92
+ * @param data 键值对对象
93
+ */
94
+ set(data: Record<string, any>): void;
95
+ }
96
+
97
+ /**
98
+ * SelfManager - 管理 self 数据的细粒度操作
99
+ */
100
+ export class SelfManager {
101
+ /**
102
+ * 添加或设置 self.public 中的键值对
103
+ * @param key 键名
104
+ * @param value 值
105
+ */
106
+ add(key: string, value: any): void;
107
+
108
+ /**
109
+ * 更新 self.public 中的键值对(与 add 相同,提供语义化接口)
110
+ * @param key 键名
111
+ * @param value 值
112
+ */
113
+ update(key: string, value: any): void;
114
+
115
+ /**
116
+ * 删除 self.public 中的键(通过设置为 null)
117
+ * @param key 键名
118
+ */
119
+ delete(key: string): void;
120
+
121
+ /**
122
+ * 批量设置多个键值对
123
+ * @param data 键值对对象
124
+ */
125
+ set(data: Record<string, any>): void;
126
+ }
127
+
65
128
  /**
66
129
  * RiffleServer - 游戏服务器 WebSocket 客户端
67
130
  */
@@ -79,18 +142,17 @@ export class RiffleServer {
79
142
  /** 是否已初始化 */
80
143
  readonly isInitialized: boolean;
81
144
 
145
+ /** World 数据管理器 */
146
+ readonly world: WorldManager;
147
+ /** Self 数据管理器 */
148
+ readonly self: SelfManager;
149
+
82
150
  /**
83
151
  * 初始化服务器(必需,连接后必须先调用)
84
152
  * @param initData 初始化数据
85
153
  */
86
154
  init(initData: InitData): void;
87
155
 
88
- /**
89
- * 更新数据(部分更新,合并到现有数据)
90
- * @param updateData 更新数据
91
- */
92
- update(updateData: UpdateData): void;
93
-
94
156
  /**
95
157
  * 监听服务器推送的最新数据
96
158
  * @param callback 回调函数,接收服务器推送的数据
package/index.js CHANGED
@@ -28,12 +28,98 @@ if (typeof window !== 'undefined' && window.WebSocket) {
28
28
  }
29
29
  }
30
30
 
31
+ /**
32
+ * WorldManager - 管理 world 数据的细粒度操作
33
+ */
34
+ class WorldManager {
35
+ constructor(server) {
36
+ this._server = server;
37
+ }
38
+
39
+ /**
40
+ * 添加或设置 world 中的键值对
41
+ * @param {string} key - 键名
42
+ * @param {any} value - 值
43
+ */
44
+ add(key, value) {
45
+ this._server.sendUpdate({ world: { [key]: value } });
46
+ }
47
+
48
+ /**
49
+ * 更新 world 中的键值对(与 add 相同,提供语义化接口)
50
+ * @param {string} key - 键名
51
+ * @param {any} value - 值
52
+ */
53
+ update(key, value) {
54
+ this._server.sendUpdate({ world: { [key]: value } });
55
+ }
56
+
57
+ /**
58
+ * 删除 world 中的键(通过设置为 null)
59
+ * @param {string} key - 键名
60
+ */
61
+ delete(key) {
62
+ this._server.sendUpdate({ world: { [key]: null } });
63
+ }
64
+
65
+ /**
66
+ * 批量设置多个键值对
67
+ * @param {Object} data - 键值对对象
68
+ */
69
+ set(data) {
70
+ this._server.sendUpdate({ world: data });
71
+ }
72
+ }
73
+
74
+ /**
75
+ * SelfManager - 管理 self 数据的细粒度操作
76
+ */
77
+ class SelfManager {
78
+ constructor(server) {
79
+ this._server = server;
80
+ }
81
+
82
+ /**
83
+ * 添加或设置 self.public 中的键值对
84
+ * @param {string} key - 键名
85
+ * @param {any} value - 值
86
+ */
87
+ add(key, value) {
88
+ this._server.sendUpdate({ self: { public: { [key]: value } } });
89
+ }
90
+
91
+ /**
92
+ * 更新 self.public 中的键值对(与 add 相同,提供语义化接口)
93
+ * @param {string} key - 键名
94
+ * @param {any} value - 值
95
+ */
96
+ update(key, value) {
97
+ this._server.sendUpdate({ self: { public: { [key]: value } } });
98
+ }
99
+
100
+ /**
101
+ * 删除 self.public 中的键(通过设置为 null)
102
+ * @param {string} key - 键名
103
+ */
104
+ delete(key) {
105
+ this._server.sendUpdate({ self: { public: { [key]: null } } });
106
+ }
107
+
108
+ /**
109
+ * 批量设置多个键值对
110
+ * @param {Object} data - 键值对对象
111
+ */
112
+ set(data) {
113
+ this._server.sendUpdate({ self: { public: data } });
114
+ }
115
+ }
116
+
31
117
  /**
32
118
  * RiffleServer - 游戏服务器 WebSocket 客户端
33
119
  *
34
120
  * @example
35
121
  * ```js
36
- * import { RiffleServer } from '@riffle/server';
122
+ * import { RiffleServer } from '@lzpenguin/server';
37
123
  *
38
124
  * const server = new RiffleServer({
39
125
  * url: 'wss://api.riffle.app',
@@ -50,22 +136,25 @@ if (typeof window !== 'undefined' && window.WebSocket) {
50
136
  *
51
137
  * // 初始化服务器(必需,连接后必须先调用)
52
138
  * server.init({
53
- * hash: 'game-version-hash-123', // 游戏版本哈希值
54
- * world: { score: 0, level: 1 }, // 世界初始数据(可选)
139
+ * hash: 'game-version-hash-123',
140
+ * world: { score: 0, level: 1 },
55
141
  * self: {
56
- * public: { name: 'Player1' }, // 公开数据(可选)
57
- * private: { health: 100 } // 私有数据(可选)
142
+ * public: { name: 'Player1', x: 100, y: 100 }
58
143
  * }
59
144
  * });
60
145
  *
61
- * // 更新数据
62
- * server.update({
63
- * world: { score: 200 },
64
- * self: {
65
- * public: { position: { x: 15, y: 25 } },
66
- * private: { health: 90 }
67
- * }
68
- * });
146
+ * // 新的细粒度操作 API
147
+ * server.world.add('score', 200); // 添加/更新 world.score
148
+ * server.world.update('level', 2); // 更新 world.level
149
+ * server.world.delete('oldKey'); // 删除 world.oldKey
150
+ *
151
+ * server.self.add('x', 150); // 添加/更新 self.public.x
152
+ * server.self.update('y', 250); // 更新 self.public.y
153
+ * server.self.delete('oldProp'); // 删除 self.public.oldProp
154
+ *
155
+ * // 批量操作
156
+ * server.world.set({ score: 200, level: 2 });
157
+ * server.self.set({ x: 150, y: 250, rotation: 90 });
69
158
  * ```
70
159
  */
71
160
  export class RiffleServer {
@@ -107,6 +196,10 @@ export class RiffleServer {
107
196
  // 当前服务器数据缓存
108
197
  this.currentData = null;
109
198
 
199
+ // 创建细粒度操作管理器
200
+ this.world = new WorldManager(this);
201
+ this.self = new SelfManager(this);
202
+
110
203
  // 连接状态(异步调用,不阻塞构造函数)
111
204
  this.connect().catch(err => {
112
205
  console.error('[RiffleServer] Initial connection failed:', err);
@@ -269,15 +362,25 @@ export class RiffleServer {
269
362
  * @param {string} initData.hash - 游戏版本哈希值(必需)
270
363
  * @param {Object} [initData.world] - 世界初始数据(可选)
271
364
  * @param {Object} [initData.self] - 玩家初始数据(可选)
272
- * @param {Object} [initData.self.public] - 公开数据(可选)
273
- * @param {Object} [initData.self.private] - 私有数据(可选)
365
+ * @param {Object} [initData.self.public] - 公开数据(可选,旧格式)
366
+ * @param {Object} [initData.self.private] - 私有数据(已弃用)
274
367
  * @example
368
+ * // 推荐:直接传递公开数据
275
369
  * server.init({
276
370
  * hash: 'game-version-hash-123',
277
371
  * world: { score: 0, level: 1 },
278
- * self: {
279
- * public: { name: 'Player1' },
280
- * private: { health: 100 }
372
+ * self: {
373
+ * public: { name: 'Player1', x: 100, y: 100 }
374
+ * }
375
+ * });
376
+ *
377
+ * @example
378
+ * // 也支持直接传递对象(服务端会将其作为 public 数据处理)
379
+ * server.init({
380
+ * hash: 'game-version-hash-123',
381
+ * world: { score: 0 },
382
+ * self: {
383
+ * public: { name: 'Player1' }
281
384
  * }
282
385
  * });
283
386
  */
@@ -316,26 +419,6 @@ export class RiffleServer {
316
419
  }
317
420
  }
318
421
 
319
- /**
320
- * 更新数据(部分更新)
321
- * @param {Object} updateData - 更新数据
322
- * @param {Object} [updateData.world] - 世界数据(可选)
323
- * @param {Object} [updateData.self] - 玩家数据(可选)
324
- * @param {Object} [updateData.self.public] - 公开数据(可选)
325
- * @param {Object} [updateData.self.private] - 私有数据(可选)
326
- * @example
327
- * server.update({
328
- * world: { score: 200 },
329
- * self: {
330
- * public: { position: { x: 15, y: 25 } },
331
- * private: { health: 90 }
332
- * }
333
- * });
334
- */
335
- update(updateData) {
336
- this.sendUpdate(updateData);
337
- }
338
-
339
422
  /**
340
423
  * 监听服务器推送的最新数据
341
424
  * @param {Function} callback - 回调函数,接收服务器推送的数据
@@ -343,8 +426,11 @@ export class RiffleServer {
343
426
  * @example
344
427
  * const unsubscribe = server.onData((data) => {
345
428
  * console.log('World:', data.world);
346
- * console.log('Self:', data.self);
429
+ * console.log('Self:', data.self); // self 现在直接是公开数据对象,与 players[i] 结构一致
347
430
  * console.log('Players:', data.players);
431
+ *
432
+ * // 例如:data.self = { id: 'abc123', name: 'Player1', x: 100, y: 200 }
433
+ * // 而 data.players[0] 也是相同结构:{ id: 'def456', name: 'Player2', x: 150, y: 250 }
348
434
  * });
349
435
  *
350
436
  * // 取消监听
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lzpenguin/server",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Riffle 游戏服务器 WebSocket 客户端 SDK",
5
5
  "license": "ISC",
6
6
  "author": "lzpenguin",