@wovoon/polaris 0.4.2 → 0.5.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/dist/client.d.ts CHANGED
@@ -70,7 +70,9 @@ export declare class PolarisClient {
70
70
  * 重连安全:同一客户端对同一房间再次调用 room(roomId) 会自动断开旧连接——
71
71
  * 做"重连房间"按钮时直接再调一次 room() 即可,无需(但也不妨)先 leave() 旧的。
72
72
  */
73
- room(roomId: string): {
73
+ room(roomId: string, options?: {
74
+ mode?: string;
75
+ }): {
74
76
  send: (data: unknown) => void;
75
77
  leave: () => void;
76
78
  onMessage: (callback: (payload: {
@@ -86,6 +88,13 @@ export declare class PolarisClient {
86
88
  add: (data: T) => Promise<PolarisDocumentView>;
87
89
  set: (id: string, data: T) => Promise<PolarisDocumentView>;
88
90
  remove: (id: string) => Promise<unknown>;
91
+ /** onChange(cb):集合被写入(玩家/云函数/权威房间)时回调 {action,id}——收到后重查。 */
92
+ onChange: (cb: (notice: {
93
+ action: string;
94
+ id: string;
95
+ }) => void) => {
96
+ close: () => void;
97
+ };
89
98
  };
90
99
  }
91
100
  export { parseData };
package/dist/client.js CHANGED
@@ -102,7 +102,7 @@ export class PolarisClient {
102
102
  * 重连安全:同一客户端对同一房间再次调用 room(roomId) 会自动断开旧连接——
103
103
  * 做"重连房间"按钮时直接再调一次 room() 即可,无需(但也不妨)先 leave() 旧的。
104
104
  */
105
- room(roomId) {
105
+ room(roomId, options) {
106
106
  if (!roomId)
107
107
  throw new PolarisError("room(roomId) 需要房间号", "WOVOON_ARGUMENT");
108
108
  this.activeRooms.get(roomId)?.leave();
@@ -121,7 +121,13 @@ export class PolarisClient {
121
121
  };
122
122
  const handle = {
123
123
  send: (data) => {
124
- if (socket && socket.readyState === 1)
124
+ if (!socket || socket.readyState !== 1)
125
+ return;
126
+ if (data instanceof ArrayBuffer)
127
+ socket.send(data);
128
+ else if (data instanceof Uint8Array)
129
+ socket.send(data.buffer);
130
+ else
125
131
  socket.send(JSON.stringify({ type: "msg", data }));
126
132
  },
127
133
  leave: () => {
@@ -143,11 +149,26 @@ export class PolarisClient {
143
149
  emit("close", { error: "当前环境不支持 WebSocket" });
144
150
  return;
145
151
  }
146
- const origin = this.baseUrl || `${location.protocol}//${location.host}`;
152
+ // CDN 加速域不支持 WebSocket:优先用入口页注入的 __wovoonRuntimeWsBase(平台主站,直连源站),
153
+ // 并以启动片段的 wovoon_play_sid 作为跨主机凭证(与 play cookie 同一游玩会话)。
154
+ const wsBase = String(globalThis.__wovoonRuntimeWsBase ?? "").replace(/\/+$/, "");
155
+ const origin = this.baseUrl || wsBase || `${location.protocol}//${location.host}`;
147
156
  const scheme = origin.startsWith("https") ? "wss" : "ws";
148
- socket = new WebSocket(`${scheme}://${origin.replace(/^https?:\/\//, "")}/api/cloud/rooms/${data.appId}/${encodeURIComponent(roomId)}`);
157
+ const roomPath = `/api/cloud/rooms/${data.appId}/${encodeURIComponent(roomId)}`;
158
+ const sid = /[?&#]wovoon_play_sid=([0-9a-fA-F-]{36})(?:&|$)/.exec(String(location.hash ?? ""))?.[1];
159
+ const query = [];
160
+ if (sid)
161
+ query.push(`wovoon_play_sid=${sid}`);
162
+ if (options?.mode)
163
+ query.push(`mode=${encodeURIComponent(options.mode)}`);
164
+ socket = new WebSocket(`${scheme}://${origin.replace(/^https?:\/\//, "")}${roomPath}${query.length ? "?" + query.join("&") : ""}`);
165
+ socket.binaryType = "arraybuffer";
149
166
  socket.onopen = () => { this.activeRooms.set(roomId, handle); };
150
167
  socket.onmessage = (event) => {
168
+ if (event.data instanceof ArrayBuffer) {
169
+ emit("message", { binary: true, data: new Uint8Array(event.data) });
170
+ return;
171
+ }
151
172
  let frame = null;
152
173
  try {
153
174
  frame = JSON.parse(String(event.data));
@@ -201,7 +222,58 @@ export class PolarisClient {
201
222
  add: (data) => docUrl().then((url) => client.request("POST", url, { data: JSON.stringify(data ?? {}) })),
202
223
  set: (id, data) => docUrl(id).then((url) => client.request("PUT", url, { data: JSON.stringify(data ?? {}) })),
203
224
  remove: (id) => docUrl(id).then((url) => client.request("DELETE", url)),
225
+ /** onChange(cb):集合被写入(玩家/云函数/权威房间)时回调 {action,id}——收到后重查。 */
226
+ onChange: (cb) => {
227
+ return watchCollections(client, name, cb);
228
+ },
204
229
  };
205
230
  }
206
231
  }
232
+ const collectionsWatch = { listeners: [], socket: null };
233
+ function attachCollectionsWatch(client) {
234
+ if (collectionsWatch.socket || typeof WebSocket === "undefined")
235
+ return;
236
+ client.session().then((data) => {
237
+ const wsBase = String(globalThis.__wovoonRuntimeWsBase ?? "").replace(/\/+$/, "");
238
+ const origin = wsBase || `${location.protocol}//${location.host}`;
239
+ const scheme = origin.startsWith("https") ? "wss" : "ws";
240
+ const sid = /[?&#]wovoon_play_sid=([0-9a-fA-F-]{36})(?:&|$)/.exec(String(location.hash ?? ""))?.[1];
241
+ const query = sid ? `?wovoon_play_sid=${sid}` : "";
242
+ const socket = new WebSocket(`${scheme}://${origin.replace(/^https?:\/\//, "")}/api/cloud/rooms/${data.appId}/__collections${query}`);
243
+ collectionsWatch.socket = socket;
244
+ socket.onmessage = (event) => {
245
+ try {
246
+ const frame = JSON.parse(String(event.data));
247
+ if (frame?.type !== "docs")
248
+ return;
249
+ for (const listener of collectionsWatch.listeners) {
250
+ if (listener.collection === frame.collection && frame.action && frame.id) {
251
+ try {
252
+ listener.cb({ action: frame.action, id: frame.id });
253
+ }
254
+ catch { /* 监听器异常不中断 */ }
255
+ }
256
+ }
257
+ }
258
+ catch { /* 非 JSON 帧忽略 */ }
259
+ };
260
+ socket.onclose = () => {
261
+ collectionsWatch.socket = null;
262
+ if (collectionsWatch.listeners.length)
263
+ setTimeout(() => attachCollectionsWatch(client), 2000);
264
+ };
265
+ }).catch(() => { });
266
+ }
267
+ function watchCollections(client, collection, cb) {
268
+ const listener = { collection, cb };
269
+ collectionsWatch.listeners.push(listener);
270
+ attachCollectionsWatch(client);
271
+ return {
272
+ close: () => {
273
+ const index = collectionsWatch.listeners.indexOf(listener);
274
+ if (index >= 0)
275
+ collectionsWatch.listeners.splice(index, 1);
276
+ },
277
+ };
278
+ }
207
279
  export { parseData };
package/dist/protocol.js CHANGED
@@ -9,7 +9,7 @@ export function isValidCollectionName(name) {
9
9
  return COLLECTION_NAME_PATTERN.test(name);
10
10
  }
11
11
  /** manifest 里单个能力名的完整合法形态(与平台 AssetSecurityScanner.parseContract 的校验一致)。 */
12
- export const CAPABILITY_PATTERN = /^(identity|function|room|collection:[a-z][a-z0-9_]{1,31})$/;
12
+ export const CAPABILITY_PATTERN = /^(identity|function|room|room:competitive|gamelogic|collection:[a-z][a-z0-9_]{1,31})$/;
13
13
  export function isValidCapability(value) {
14
14
  return CAPABILITY_PATTERN.test(value);
15
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wovoon/polaris",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "wovoon Polaris SDK——wovoon 大后台云能力客户端(身份/数据集合/云函数/房间实时),协议 1",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",