@cloudcare/cocos-sdk 0.1.0-alpha.3 → 0.1.0-alpha.4

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 (73) hide show
  1. package/README.md +1 -1
  2. package/bin/cli.cjs +3 -0
  3. package/components/creator2/ReplayPrivacy.ts +23 -0
  4. package/components/creator3/ReplayPrivacy.ts +25 -0
  5. package/dist/creator2/core/replay-async-transport.d.ts +4 -0
  6. package/dist/creator2/core/replay-async-transport.d.ts.map +1 -0
  7. package/dist/creator2/core/replay-async-transport.js +67 -0
  8. package/dist/creator2/core/replay-async-transport.js.map +1 -0
  9. package/dist/creator2/core/replay-file.d.ts +5 -0
  10. package/dist/creator2/core/replay-file.d.ts.map +1 -0
  11. package/dist/creator2/core/replay-file.js +101 -0
  12. package/dist/creator2/core/replay-file.js.map +1 -0
  13. package/dist/creator2/core/replay-privacy-nodes.d.ts +14 -0
  14. package/dist/creator2/core/replay-privacy-nodes.d.ts.map +1 -0
  15. package/dist/creator2/core/replay-privacy-nodes.js +18 -0
  16. package/dist/creator2/core/replay-privacy-nodes.js.map +1 -0
  17. package/dist/creator2/core/replay.d.ts +5 -3
  18. package/dist/creator2/core/replay.d.ts.map +1 -1
  19. package/dist/creator2/core/replay.js +74 -41
  20. package/dist/creator2/core/replay.js.map +1 -1
  21. package/dist/creator2/core/transport.d.ts +1 -0
  22. package/dist/creator2/core/transport.d.ts.map +1 -1
  23. package/dist/creator2/core/transport.js.map +1 -1
  24. package/dist/creator2/core/version.d.ts +1 -1
  25. package/dist/creator2/core/version.js +1 -1
  26. package/dist/creator2/creator2/capture.d.ts +1 -1
  27. package/dist/creator2/creator2/capture.d.ts.map +1 -1
  28. package/dist/creator2/creator2/capture.js +9 -16
  29. package/dist/creator2/creator2/capture.js.map +1 -1
  30. package/dist/creator2/creator2/transport.d.ts +1 -0
  31. package/dist/creator2/creator2/transport.d.ts.map +1 -1
  32. package/dist/creator2/creator2/transport.js +4 -0
  33. package/dist/creator2/creator2/transport.js.map +1 -1
  34. package/dist/creator3/core/replay-async-transport.d.ts +4 -0
  35. package/dist/creator3/core/replay-async-transport.d.ts.map +1 -0
  36. package/dist/creator3/core/replay-async-transport.js +16 -0
  37. package/dist/creator3/core/replay-async-transport.js.map +1 -0
  38. package/dist/creator3/core/replay-file.d.ts +5 -0
  39. package/dist/creator3/core/replay-file.d.ts.map +1 -0
  40. package/dist/creator3/core/replay-file.js +31 -0
  41. package/dist/creator3/core/replay-file.js.map +1 -0
  42. package/dist/creator3/core/replay-privacy-nodes.d.ts +14 -0
  43. package/dist/creator3/core/replay-privacy-nodes.d.ts.map +1 -0
  44. package/dist/creator3/core/replay-privacy-nodes.js +15 -0
  45. package/dist/creator3/core/replay-privacy-nodes.js.map +1 -0
  46. package/dist/creator3/core/replay.d.ts +5 -3
  47. package/dist/creator3/core/replay.d.ts.map +1 -1
  48. package/dist/creator3/core/replay.js +23 -9
  49. package/dist/creator3/core/replay.js.map +1 -1
  50. package/dist/creator3/core/transport.d.ts +1 -0
  51. package/dist/creator3/core/transport.d.ts.map +1 -1
  52. package/dist/creator3/core/transport.js.map +1 -1
  53. package/dist/creator3/core/version.d.ts +1 -1
  54. package/dist/creator3/core/version.js +1 -1
  55. package/dist/creator3/creator3/capture.d.ts +1 -1
  56. package/dist/creator3/creator3/capture.d.ts.map +1 -1
  57. package/dist/creator3/creator3/capture.js +6 -13
  58. package/dist/creator3/creator3/capture.js.map +1 -1
  59. package/dist/creator3/creator3/transport.d.ts +1 -0
  60. package/dist/creator3/creator3/transport.d.ts.map +1 -1
  61. package/dist/creator3/creator3/transport.js +4 -0
  62. package/dist/creator3/creator3/transport.js.map +1 -1
  63. package/extensions/creator2/install-native.cjs +53 -1
  64. package/extensions/creator3/install-native.cjs +53 -1
  65. package/native/android/src/main/java/com/ft/sdk/cocos/FTCocosBridge.java +4 -0
  66. package/native/android/src/main/java/com/ft/sdk/cocos/FTCocosReplayImageJobs.java +55 -0
  67. package/native/ios/FTCocosBridge.m +17 -0
  68. package/native/ios/FTCocosBridge.podspec +2 -2
  69. package/native/ios/FTCocosReplayImageJobs.h +11 -0
  70. package/native/ios/FTCocosReplayImageJobs.m +54 -0
  71. package/native/shared/FTReplayFileBridge.h +62 -0
  72. package/native/shared/FTReplayFileWorker.h +88 -0
  73. package/package.json +2 -1
@@ -1,6 +1,8 @@
1
1
  import { Camera, Director, EditBox, RenderTexture, UITransform, Vec3, director, native, sys, view } from 'cc';
2
2
  import { frameFingerprint } from '../core/replay.js';
3
+ import { persistReplayFrame, disposeReplayFrame } from '../core/replay-file.js';
3
4
  import { projectPrivacyBounds } from '../core/replay-privacy.js';
5
+ import { collectReplayPrivacyNodes } from '../core/replay-privacy-nodes.js';
4
6
  import { waitForRenderTextureReadback } from './replay-render-cycle.js';
5
7
  export class FTCreator3CanvasCapture {
6
8
  constructor() {
@@ -64,23 +66,14 @@ export class FTCreator3CanvasCapture {
64
66
  };
65
67
  }
66
68
  async persist(frame, fingerprint = frameFingerprint(frame.rgba)) {
67
- const path = `${native.fileUtils.getWritablePath()}cocos-sdk-replay-${fingerprint}.rgba`;
68
- if (!native.fileUtils.writeDataToFile(frame.rgba, path)) {
69
- throw new Error(`Unable to persist replay frame: ${path}`);
70
- }
71
- return { path, width: frame.width, height: frame.height, timestamp: frame.timestamp, fingerprint };
69
+ return persistReplayFrame(frame, fingerprint, native.fileUtils.getWritablePath());
72
70
  }
73
71
  disposeStoredFrame(frame) {
74
- if (native.fileUtils.isFileExist(frame.path))
75
- native.fileUtils.removeFile(frame.path);
72
+ return disposeReplayFrame(frame);
76
73
  }
77
74
  collectPrivacyRegions(camera, width, height) {
78
- var _a, _b;
79
- const nodes = new Map(this.privacy);
80
- (_b = (_a = director.getScene()) === null || _a === void 0 ? void 0 : _a.getComponentsInChildren(EditBox)) === null || _b === void 0 ? void 0 : _b.forEach((editBox) => {
81
- if (!nodes.has(editBox.node))
82
- nodes.set(editBox.node, 'mask');
83
- });
75
+ const scene = director.getScene();
76
+ const nodes = collectReplayPrivacyNodes(this.privacy, (scene === null || scene === void 0 ? void 0 : scene.getComponentsInChildren('ReplayPrivacy')) || [], (scene === null || scene === void 0 ? void 0 : scene.getComponentsInChildren(EditBox)) || []);
84
77
  const regions = [];
85
78
  nodes.forEach((mode, node) => {
86
79
  var _a, _b, _c, _d;
@@ -1 +1 @@
1
- {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../../../../src/creator3/capture.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../../../../src/creator3/capture.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { type FTNativeTransport } from '../core/transport.js';
2
2
  import type { FTValue } from '../core/types.js';
3
3
  export declare class FTCreator3Transport implements FTNativeTransport {
4
4
  readonly platform: 'android' | 'ios' | 'unsupported';
5
+ invokeAsync<T = FTValue>(method: string, payload?: unknown): Promise<T | undefined>;
5
6
  constructor();
6
7
  invoke<T = FTValue>(method: string, payload?: unknown): T | undefined;
7
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../../../src/creator3/transport.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,qBAAa,mBAAoB,YAAW,iBAAiB;IAC3D,QAAQ,CAAC,QAAQ,EAAE,SAAS,GAAG,KAAK,GAAG,aAAa,CAAC;;IAYrD,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,GAAG,SAAS;CActE"}
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../../../src/creator3/transport.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAGhD,qBAAa,mBAAoB,YAAW,iBAAiB;IAC3D,QAAQ,CAAC,QAAQ,EAAE,SAAS,GAAG,KAAK,GAAG,aAAa,CAAC;IAErD,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;;IAcnF,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,GAAG,SAAS;CActE"}
@@ -1,7 +1,11 @@
1
1
  import { native, sys } from 'cc';
2
2
  import { NATIVE } from 'cc/env';
3
3
  import { parseTransportResponse } from '../core/transport.js';
4
+ import { invokeReplayAsync } from '../core/replay-async-transport.js';
4
5
  export class FTCreator3Transport {
6
+ invokeAsync(method, payload) {
7
+ return invokeReplayAsync(this, method, payload);
8
+ }
5
9
  constructor() {
6
10
  this.platform = !NATIVE
7
11
  ? 'unsupported'
@@ -1 +1 @@
1
- {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../../src/creator3/transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,IAAI,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAA0B,MAAM,sBAAsB,CAAC;AAGtF,MAAM,OAAO,mBAAmB;IAG9B;QACE,IAAI,CAAC,QAAQ,GAAG,CAAC,MAAM;YACrB,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,OAAO;gBACzB,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,GAAG;oBACrB,CAAC,CAAC,KAAK;oBACP,CAAC,CAAC,aAAa,CAAC;IACxB,CAAC;IAED,MAAM,CAAc,MAAc,EAAE,OAAiB;QACnD,IAAI,IAAI,CAAC,QAAQ,KAAK,aAAa;YAAE,OAAO,SAAS,CAAC;QACtD,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,KAAK,SAAS;YACrC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAChC,gCAAgC,EAChC,QAAQ,EACR,0DAA0D,EAC1D,MAAM,EACN,IAAI,CACL;YACH,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,eAAe,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACzF,OAAO,sBAAsB,CAAI,GAAG,CAAC,CAAC;IACxC,CAAC;CACF"}
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../../src/creator3/transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,IAAI,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAA0B,MAAM,sBAAsB,CAAC;AAEtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,MAAM,OAAO,mBAAmB;IAG9B,WAAW,CAAc,MAAc,EAAE,OAAiB;QACxD,OAAO,iBAAiB,CAAI,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC;IAED;QACE,IAAI,CAAC,QAAQ,GAAG,CAAC,MAAM;YACrB,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,OAAO;gBACzB,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,GAAG;oBACrB,CAAC,CAAC,KAAK;oBACP,CAAC,CAAC,aAAa,CAAC;IACxB,CAAC;IAED,MAAM,CAAc,MAAc,EAAE,OAAiB;QACnD,IAAI,IAAI,CAAC,QAAQ,KAAK,aAAa;YAAE,OAAO,SAAS,CAAC;QACtD,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,KAAK,SAAS;YACrC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAChC,gCAAgC,EAChC,QAAQ,EACR,0DAA0D,EAC1D,MAAM,EACN,IAAI,CACL;YACH,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,eAAe,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACzF,OAAO,sBAAsB,CAAI,GAAG,CAAC,CAAC;IACxC,CAAC;CACF"}
@@ -30,6 +30,11 @@ function installNative(buildRoot, extensionRoot, logger = console) {
30
30
  const buildNative = path.join(buildRoot, 'cocos-sdk-native');
31
31
  copyDirectory(nativeSource, buildNative);
32
32
  const files = findFiles(buildRoot, 7);
33
+ const replayHeader = path.join(buildNative, 'shared', 'FTReplayFileBridge.h');
34
+ if (fs.existsSync(replayHeader)) {
35
+ findReplayEntryFiles(files)
36
+ .forEach((file) => patchReplayFileBridge(file, replayHeader));
37
+ }
33
38
  const gradlePropertiesFiles = files.filter((file) => path.basename(file) === 'gradle.properties');
34
39
  const gradleFiles = uniqueFiles([
35
40
  ...files.filter((file) => /(?:^|\/)app\/build\.gradle$/.test(normalize(file))),
@@ -61,6 +66,53 @@ function installNative(buildRoot, extensionRoot, logger = console) {
61
66
  );
62
67
  }
63
68
 
69
+ function patchReplayFileBridge(file, header) {
70
+ const original = fs.readFileSync(file, 'utf8');
71
+ const includeBegin = '// COCOS_SDK_REPLAY_INCLUDE_BEGIN';
72
+ const includeEnd = '// COCOS_SDK_REPLAY_INCLUDE_END';
73
+ const initBegin = '// COCOS_SDK_REPLAY_INIT_BEGIN';
74
+ const initEnd = '// COCOS_SDK_REPLAY_INIT_END';
75
+ // Creator 3 shares Game.cpp across iOS/Android builds. Keep its include stable
76
+ // when another platform's build directory is cleaned or regenerated.
77
+ const sharedDirectory = path.join(path.dirname(file), 'cocos-sdk-replay');
78
+ const include = `${includeBegin}\n#include "cocos-sdk-replay/FTReplayFileBridge.h"\n${includeEnd}`;
79
+ const init = `${initBegin}\n ft_cocos::installReplayFileBridge();\n ${initEnd}\n `;
80
+ let next = original;
81
+ if (markedPattern(initBegin, initEnd).test(next)) {
82
+ next = next.replace(markedPattern(initBegin, initEnd), init.trimEnd());
83
+ } else {
84
+ const anchor = path.basename(file) === 'Game.cpp'
85
+ ? /\b(?:return\s+)?BaseGame::init\(\);/
86
+ : /\b(?:return\s+)?se->start\(\);/;
87
+ if (!anchor.test(next)) return;
88
+ next = next.replace(anchor, (value) => init + value);
89
+ }
90
+ next = markedPattern(includeBegin, includeEnd).test(next)
91
+ ? next.replace(markedPattern(includeBegin, includeEnd), include)
92
+ : `${include}\n${next}`;
93
+ copyDirectory(path.dirname(header), sharedDirectory);
94
+ if (next !== original) fs.writeFileSync(file, next);
95
+ }
96
+
97
+ function findReplayEntryFiles(files) {
98
+ const entries = files.filter((file) => ['AppDelegate.cpp', 'Game.cpp'].includes(path.basename(file)));
99
+ for (const file of files) {
100
+ let nativeDirectory;
101
+ if (path.basename(file) === 'gradle.properties') {
102
+ const value = readProperty(fs.readFileSync(file, 'utf8'), 'NATIVE_DIR');
103
+ if (value) nativeDirectory = path.resolve(path.dirname(file), value);
104
+ } else if (path.basename(file) === 'CMakeCache.txt') {
105
+ const match = fs.readFileSync(file, 'utf8').match(/^CMAKE_HOME_DIRECTORY:INTERNAL=(.+)$/m);
106
+ if (match) nativeDirectory = match[1].trim();
107
+ }
108
+ if (nativeDirectory) {
109
+ const game = path.resolve(nativeDirectory, '..', 'common', 'Classes', 'Game.cpp');
110
+ if (fs.existsSync(game)) entries.push(game);
111
+ }
112
+ }
113
+ return uniqueFiles(entries);
114
+ }
115
+
64
116
  function patchCocos2IosConfiguration(projectFile, target) {
65
117
  const projectDirectory = path.dirname(path.dirname(projectFile));
66
118
  const iosDirectory = path.join(projectDirectory, 'ios');
@@ -202,7 +254,7 @@ function patchGradle(file, nativeRoot) {
202
254
  'dependencies {',
203
255
  " implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-sdk:1.7.6-alpha02'",
204
256
  " implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-native:1.1.3'",
205
- " implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-session-replay:0.1.9-alpha02'",
257
+ " implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-session-replay:0.1.9-alpha03'",
206
258
  " implementation 'com.google.code.gson:gson:2.10.1'",
207
259
  " implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.22')",
208
260
  " implementation 'androidx.appcompat:appcompat:1.1.0'",
@@ -30,6 +30,11 @@ function installNative(buildRoot, extensionRoot, logger = console) {
30
30
  const buildNative = path.join(buildRoot, 'cocos-sdk-native');
31
31
  copyDirectory(nativeSource, buildNative);
32
32
  const files = findFiles(buildRoot, 7);
33
+ const replayHeader = path.join(buildNative, 'shared', 'FTReplayFileBridge.h');
34
+ if (fs.existsSync(replayHeader)) {
35
+ findReplayEntryFiles(files)
36
+ .forEach((file) => patchReplayFileBridge(file, replayHeader));
37
+ }
33
38
  const gradlePropertiesFiles = files.filter((file) => path.basename(file) === 'gradle.properties');
34
39
  const gradleFiles = uniqueFiles([
35
40
  ...files.filter((file) => /(?:^|\/)app\/build\.gradle$/.test(normalize(file))),
@@ -61,6 +66,53 @@ function installNative(buildRoot, extensionRoot, logger = console) {
61
66
  );
62
67
  }
63
68
 
69
+ function patchReplayFileBridge(file, header) {
70
+ const original = fs.readFileSync(file, 'utf8');
71
+ const includeBegin = '// COCOS_SDK_REPLAY_INCLUDE_BEGIN';
72
+ const includeEnd = '// COCOS_SDK_REPLAY_INCLUDE_END';
73
+ const initBegin = '// COCOS_SDK_REPLAY_INIT_BEGIN';
74
+ const initEnd = '// COCOS_SDK_REPLAY_INIT_END';
75
+ // Creator 3 shares Game.cpp across iOS/Android builds. Keep its include stable
76
+ // when another platform's build directory is cleaned or regenerated.
77
+ const sharedDirectory = path.join(path.dirname(file), 'cocos-sdk-replay');
78
+ const include = `${includeBegin}\n#include "cocos-sdk-replay/FTReplayFileBridge.h"\n${includeEnd}`;
79
+ const init = `${initBegin}\n ft_cocos::installReplayFileBridge();\n ${initEnd}\n `;
80
+ let next = original;
81
+ if (markedPattern(initBegin, initEnd).test(next)) {
82
+ next = next.replace(markedPattern(initBegin, initEnd), init.trimEnd());
83
+ } else {
84
+ const anchor = path.basename(file) === 'Game.cpp'
85
+ ? /\b(?:return\s+)?BaseGame::init\(\);/
86
+ : /\b(?:return\s+)?se->start\(\);/;
87
+ if (!anchor.test(next)) return;
88
+ next = next.replace(anchor, (value) => init + value);
89
+ }
90
+ next = markedPattern(includeBegin, includeEnd).test(next)
91
+ ? next.replace(markedPattern(includeBegin, includeEnd), include)
92
+ : `${include}\n${next}`;
93
+ copyDirectory(path.dirname(header), sharedDirectory);
94
+ if (next !== original) fs.writeFileSync(file, next);
95
+ }
96
+
97
+ function findReplayEntryFiles(files) {
98
+ const entries = files.filter((file) => ['AppDelegate.cpp', 'Game.cpp'].includes(path.basename(file)));
99
+ for (const file of files) {
100
+ let nativeDirectory;
101
+ if (path.basename(file) === 'gradle.properties') {
102
+ const value = readProperty(fs.readFileSync(file, 'utf8'), 'NATIVE_DIR');
103
+ if (value) nativeDirectory = path.resolve(path.dirname(file), value);
104
+ } else if (path.basename(file) === 'CMakeCache.txt') {
105
+ const match = fs.readFileSync(file, 'utf8').match(/^CMAKE_HOME_DIRECTORY:INTERNAL=(.+)$/m);
106
+ if (match) nativeDirectory = match[1].trim();
107
+ }
108
+ if (nativeDirectory) {
109
+ const game = path.resolve(nativeDirectory, '..', 'common', 'Classes', 'Game.cpp');
110
+ if (fs.existsSync(game)) entries.push(game);
111
+ }
112
+ }
113
+ return uniqueFiles(entries);
114
+ }
115
+
64
116
  function patchCocos2IosConfiguration(projectFile, target) {
65
117
  const projectDirectory = path.dirname(path.dirname(projectFile));
66
118
  const iosDirectory = path.join(projectDirectory, 'ios');
@@ -202,7 +254,7 @@ function patchGradle(file, nativeRoot) {
202
254
  'dependencies {',
203
255
  " implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-sdk:1.7.6-alpha02'",
204
256
  " implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-native:1.1.3'",
205
- " implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-session-replay:0.1.9-alpha02'",
257
+ " implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-session-replay:0.1.9-alpha03'",
206
258
  " implementation 'com.google.code.gson:gson:2.10.1'",
207
259
  " implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.22')",
208
260
  " implementation 'androidx.appcompat:appcompat:1.1.0'",
@@ -124,6 +124,10 @@ public final class FTCocosBridge {
124
124
 
125
125
  private static Object dispatch(String method, JSONObject payload) throws Exception {
126
126
  switch (method) {
127
+ case "replay.beginSaveImage":
128
+ return FTCocosReplayImageJobs.begin(payload);
129
+ case "replay.pollSaveImage":
130
+ return FTCocosReplayImageJobs.poll(payload.getString("job"));
127
131
  case "hybrid.attach":
128
132
  attachHybrid(payload);
129
133
  return null;
@@ -0,0 +1,55 @@
1
+ package com.ft.sdk.cocos;
2
+
3
+ import org.json.JSONObject;
4
+ import java.util.UUID;
5
+ import java.util.concurrent.ExecutorService;
6
+ import java.util.concurrent.Executors;
7
+
8
+ /** One image in flight across JS runtimes; the caller polls without waiting on disk/encoding. */
9
+ final class FTCocosReplayImageJobs {
10
+ private static final ExecutorService worker = Executors.newSingleThreadExecutor(runnable -> {
11
+ Thread thread = new Thread(runnable, "cocos-replay-image");
12
+ thread.setDaemon(true);
13
+ return thread;
14
+ });
15
+ private static Job current;
16
+
17
+ private static final class Job {
18
+ final String id = UUID.randomUUID().toString();
19
+ String response;
20
+ }
21
+
22
+ static synchronized String begin(JSONObject arguments) throws Exception {
23
+ final String method = arguments.getString("method");
24
+ if (!"replay.saveImage".equals(method) && !"replay.saveImageV2".equals(method)) {
25
+ throw new IllegalArgumentException("Unsupported asynchronous Replay method");
26
+ }
27
+ final String payload = arguments.getJSONObject("arguments").toString();
28
+ if (current != null && current.response == null) throw new IllegalStateException("Replay encoder is busy");
29
+ final Job job = new Job();
30
+ current = job;
31
+ try {
32
+ worker.execute(() -> {
33
+ String response = FTCocosBridge.invokeLocal(method, payload);
34
+ synchronized (FTCocosReplayImageJobs.class) {
35
+ job.response = response;
36
+ }
37
+ });
38
+ } catch (RuntimeException error) {
39
+ current = null;
40
+ throw error;
41
+ }
42
+ return job.id;
43
+ }
44
+
45
+ static synchronized JSONObject poll(String id) throws Exception {
46
+ if (current == null || !current.id.equals(id)) throw new IllegalArgumentException("Replay encoding job is unavailable");
47
+ JSONObject result = new JSONObject();
48
+ result.put("pending", current.response == null);
49
+ if (current.response != null) {
50
+ result.put("response", current.response);
51
+ current = null;
52
+ }
53
+ return result;
54
+ }
55
+ }
@@ -1,4 +1,5 @@
1
1
  #import "FTCocosBridge.h"
2
+ #import "FTCocosReplayImageJobs.h"
2
3
 
3
4
  #import "FTMobileSDK.h"
4
5
  #import "FTSessionReplay.h"
@@ -24,6 +25,10 @@
24
25
  }
25
26
 
26
27
  + (id)dispatch:(NSString *)method arguments:(NSDictionary *)arguments {
28
+ if ([method isEqualToString:@"replay.beginSaveImage"]) return [self beginSaveImage:arguments];
29
+ if ([method isEqualToString:@"replay.pollSaveImage"]) {
30
+ return [FTCocosReplayImageJobs poll:[self requiredText:arguments key:@"job"]];
31
+ }
27
32
  FTExternalDataManager *rum = [FTExternalDataManager sharedManager];
28
33
  if ([method isEqualToString:@"hybrid.attach"]) {
29
34
  [self ensureNativeHostInitialized];
@@ -254,6 +259,18 @@
254
259
  return nil;
255
260
  }
256
261
 
262
+ + (NSString *)beginSaveImage:(NSDictionary *)arguments {
263
+ NSString *method = [self requiredText:arguments key:@"method"];
264
+ if (![method isEqualToString:@"replay.saveImage"] && ![method isEqualToString:@"replay.saveImageV2"]) {
265
+ [self fail:@"Unsupported asynchronous Replay method"];
266
+ }
267
+ NSDictionary *payload = [[self dictionary:arguments[@"arguments"]] copy];
268
+ if (!payload) [self fail:@"Replay image arguments are required"];
269
+ return [FTCocosReplayImageJobs beginWithWork:^NSString *{
270
+ return [self responseWithValue:[self dispatch:method arguments:payload] error:nil];
271
+ }];
272
+ }
273
+
257
274
  + (void)ensureNativeHostInitialized {
258
275
  @try {
259
276
  if (![FTMobileAgent sharedInstance]) {
@@ -1,13 +1,13 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'FTCocosBridge'
3
- s.version = '0.1.0-alpha.3'
3
+ s.version = '0.1.0-alpha.4'
4
4
  s.summary = 'CloudCare Mobile SDK native bridge for Cocos Creator.'
5
5
  s.homepage = 'https://github.com/GuanceCloud/datakit-cocos'
6
6
  s.license = { :type => 'Apache-2.0' }
7
7
  s.author = { 'Guance Cloud' => 'support@guance.com' }
8
8
  s.source = { :path => '.' }
9
9
  s.platform = :ios, '12.0'
10
- s.source_files = 'FTCocosBridge.{h,m}'
10
+ s.source_files = 'FTCocosBridge.{h,m}', 'FTCocosReplayImageJobs.{h,m}'
11
11
  s.public_header_files = 'FTCocosBridge.h'
12
12
  s.requires_arc = true
13
13
  # Cocos resolves FTCocosBridge by class name at runtime, so no direct symbol
@@ -0,0 +1,11 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ /** One background image operation, with nonblocking result retrieval from JSB. */
6
+ @interface FTCocosReplayImageJobs : NSObject
7
+ + (NSString *)beginWithWork:(NSString *(^)(void))work;
8
+ + (NSDictionary *)poll:(NSString *)identifier;
9
+ @end
10
+
11
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,54 @@
1
+ #import "FTCocosReplayImageJobs.h"
2
+
3
+ @interface FTCocosReplayImageJob : NSObject
4
+ @property (nonatomic, copy) NSString *identifier;
5
+ @property (nonatomic, copy) NSString *response;
6
+ @end
7
+ @implementation FTCocosReplayImageJob
8
+ @end
9
+
10
+ static FTCocosReplayImageJob *currentJob;
11
+
12
+ @implementation FTCocosReplayImageJobs
13
+
14
+ + (NSString *)beginWithWork:(NSString *(^)(void))work {
15
+ FTCocosReplayImageJob *job = [FTCocosReplayImageJob new];
16
+ job.identifier = NSUUID.UUID.UUIDString;
17
+ @synchronized (self) {
18
+ if (currentJob && !currentJob.response) {
19
+ [NSException raise:NSInternalInconsistencyException format:@"Replay encoder is busy"];
20
+ }
21
+ currentJob = job;
22
+ }
23
+ dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
24
+ @autoreleasepool {
25
+ NSString *response;
26
+ @try {
27
+ response = work();
28
+ } @catch (NSException *exception) {
29
+ NSData *data = [NSJSONSerialization dataWithJSONObject:@{
30
+ @"ok": @NO, @"error": exception.reason ?: @"Replay encoding failed"
31
+ } options:0 error:nil];
32
+ response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
33
+ }
34
+ @synchronized (self) {
35
+ job.response = response ?: @"{\"ok\":false,\"error\":\"Empty Replay encoding result\"}";
36
+ }
37
+ }
38
+ });
39
+ return job.identifier;
40
+ }
41
+
42
+ + (NSDictionary *)poll:(NSString *)identifier {
43
+ @synchronized (self) {
44
+ if (![currentJob.identifier isEqualToString:identifier]) {
45
+ [NSException raise:NSInvalidArgumentException format:@"Replay encoding job is unavailable"];
46
+ }
47
+ if (!currentJob.response) return @{ @"pending": @YES };
48
+ NSDictionary *result = @{ @"pending": @NO, @"response": currentJob.response };
49
+ currentJob = nil;
50
+ return result;
51
+ }
52
+ }
53
+
54
+ @end
@@ -0,0 +1,62 @@
1
+ #pragma once
2
+
3
+ #if __has_include("bindings/jswrapper/SeApi.h")
4
+ #define FT_COCOS_CREATOR3 1
5
+ #include "bindings/jswrapper/SeApi.h"
6
+ #else
7
+ #include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
8
+ #endif
9
+ #include "FTReplayFileWorker.h"
10
+
11
+ namespace ft_cocos {
12
+
13
+ static ReplayFileWorker replayFiles;
14
+
15
+ static bool writeReplayFile(se::State& state) {
16
+ const auto& args = state.args();
17
+ if (args.size() != 2 || !args[0].isObject() || !args[1].isString()) return false;
18
+ auto* object = args[0].toObject();
19
+ if (!object->isTypedArray()) return false;
20
+ uint8_t* bytes = nullptr;
21
+ size_t size = 0;
22
+ if (!object->getTypedArrayData(&bytes, &size)) return false;
23
+ state.rval().setBoolean(replayFiles.write(bytes, size, args[1].toString()));
24
+ return true;
25
+ }
26
+ SE_BIND_FUNC(writeReplayFile)
27
+
28
+ static bool pollReplayFile(se::State& state) {
29
+ state.rval().setString(replayFiles.poll());
30
+ return true;
31
+ }
32
+ SE_BIND_FUNC(pollReplayFile)
33
+
34
+ static bool removeReplayFile(se::State& state) {
35
+ const auto& args = state.args();
36
+ if (args.size() != 1 || !args[0].isString()) return false;
37
+ state.rval().setBoolean(replayFiles.remove(args[0].toString()));
38
+ return true;
39
+ }
40
+ SE_BIND_FUNC(removeReplayFile)
41
+
42
+ static bool registerReplayFileBridge(se::Object* global) {
43
+ replayFiles.cancel();
44
+ const se::HandleObject bridge(se::Object::createPlainObject());
45
+ bridge->defineFunction("write", _SE(writeReplayFile));
46
+ bridge->defineFunction("poll", _SE(pollReplayFile));
47
+ bridge->defineFunction("remove", _SE(removeReplayFile));
48
+ global->setProperty("__ftCocosReplayFile", se::Value(bridge));
49
+ se::ScriptEngine::getInstance()->addBeforeCleanupHook([]() { replayFiles.cancel(); });
50
+ return true;
51
+ }
52
+
53
+ static void installReplayFileBridge() {
54
+ #if FT_COCOS_CREATOR3
55
+ se::ScriptEngine::getInstance()->addPermanentRegisterCallback(registerReplayFileBridge);
56
+ #else
57
+ // Creator 2 calls AppDelegate again when restarting its script VM.
58
+ se::ScriptEngine::getInstance()->addRegisterCallback(registerReplayFileBridge);
59
+ #endif
60
+ }
61
+
62
+ } // namespace ft_cocos
@@ -0,0 +1,88 @@
1
+ #pragma once
2
+
3
+ #include <atomic>
4
+ #include <cerrno>
5
+ #include <cstdio>
6
+ #include <memory>
7
+ #include <string>
8
+ #include <thread>
9
+ #include <vector>
10
+
11
+ namespace ft_cocos {
12
+
13
+ // Accessed only by the script thread. Workers own their buffers and never touch JS.
14
+ class ReplayFileWorker {
15
+ enum Status { Pending, Written, Removed, Error };
16
+ struct Job {
17
+ std::string path;
18
+ std::atomic<Status> status{Pending};
19
+ std::atomic<bool> cancelled{false};
20
+ };
21
+ std::shared_ptr<Job> current;
22
+
23
+ public:
24
+ bool write(const unsigned char* bytes, size_t size, const std::string& path) {
25
+ if (current && current->cancelled.load() && current->status.load() != Pending) current.reset();
26
+ if (current || !bytes || size == 0 || size > 2048U * 2048U * 4U || path.empty()) return false;
27
+ try {
28
+ auto job = std::make_shared<Job>();
29
+ job->path = path;
30
+ // This bounded copy is necessary because the JS buffer can be collected/reused.
31
+ std::vector<unsigned char> pixels(bytes, bytes + size);
32
+ current = job;
33
+ std::thread([job](std::vector<unsigned char> data) {
34
+ FILE* file = std::fopen(job->path.c_str(), "wb");
35
+ bool ok = false;
36
+ if (file) {
37
+ const bool written = std::fwrite(data.data(), 1, data.size(), file) == data.size();
38
+ ok = std::fclose(file) == 0 && written;
39
+ }
40
+ if (!ok || job->cancelled.load()) std::remove(job->path.c_str());
41
+ job->status.store(ok ? Written : Error);
42
+ // Covers cleanup racing with the final status transition.
43
+ if (job->cancelled.load()) std::remove(job->path.c_str());
44
+ }, std::move(pixels)).detach();
45
+ } catch (...) {
46
+ current.reset();
47
+ return false;
48
+ }
49
+ return true;
50
+ }
51
+
52
+ bool remove(const std::string& path) {
53
+ if (!current || current->path != path || current->status.load() != Written) return false;
54
+ auto job = current;
55
+ job->status.store(Pending);
56
+ try {
57
+ std::thread([job]() {
58
+ const bool ok = std::remove(job->path.c_str()) == 0 || errno == ENOENT;
59
+ job->status.store(ok ? Removed : Error);
60
+ }).detach();
61
+ } catch (...) {
62
+ job->status.store(Written);
63
+ return false;
64
+ }
65
+ return true;
66
+ }
67
+
68
+ const char* poll() {
69
+ if (!current) return "error";
70
+ switch (current->status.load()) {
71
+ case Pending: return "pending";
72
+ case Written: return "written";
73
+ case Removed: current.reset(); return "removed";
74
+ case Error: current.reset(); return "error";
75
+ }
76
+ return "error";
77
+ }
78
+
79
+ void cancel() {
80
+ if (!current) return;
81
+ auto job = current;
82
+ job->cancelled.store(true);
83
+ if (job->status.load() == Written) remove(job->path);
84
+ // Keep the slot occupied until its worker exits, including across VM restarts.
85
+ }
86
+ };
87
+
88
+ } // namespace ft_cocos
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudcare/cocos-sdk",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "Observability SDK integration for Cocos Creator 2.4 and 3.x",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Guance Cloud",
@@ -37,6 +37,7 @@
37
37
  "creator2",
38
38
  "creator3",
39
39
  "dist",
40
+ "components",
40
41
  "extensions",
41
42
  "native",
42
43
  "bin",