@playcraft/build 0.0.36 → 0.0.38
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.
|
@@ -1029,8 +1029,19 @@ export class PlayableScriptsAdapter {
|
|
|
1029
1029
|
}, options.timeout);
|
|
1030
1030
|
// shell: true 时 Node.js 把 cmd + args 拼接成 shell 字符串,
|
|
1031
1031
|
// 但不会转义 args 中的特殊字符(如 URL 里的 &)。
|
|
1032
|
-
//
|
|
1033
|
-
|
|
1032
|
+
// 需要按平台分别处理:
|
|
1033
|
+
// - Windows (cmd.exe) 不认识单引号,用双引号;cmd 的特殊字符用 ^ 转义
|
|
1034
|
+
// - POSIX (bash/sh) 使用单引号强引用
|
|
1035
|
+
const isWindows = process.platform === 'win32';
|
|
1036
|
+
const shellSafeArgs = args.map((a) => {
|
|
1037
|
+
if (isWindows) {
|
|
1038
|
+
// cmd.exe:用双引号包裹,内部的 " 转义为 "",其它 shell 元字符用 ^ 转义
|
|
1039
|
+
const escaped = a.replace(/"/g, '""').replace(/([&|<>^%])/g, '^$1');
|
|
1040
|
+
return `"${escaped}"`;
|
|
1041
|
+
}
|
|
1042
|
+
// POSIX:单引号包裹,单引号本身通过 '\'' 断开转义
|
|
1043
|
+
return `'${a.replace(/'/g, "'\\''")}'`;
|
|
1044
|
+
});
|
|
1034
1045
|
const child = spawn(cmd, shellSafeArgs, {
|
|
1035
1046
|
cwd: options.cwd,
|
|
1036
1047
|
env: options.env,
|
package/dist/platforms/base.d.ts
CHANGED
|
@@ -70,14 +70,14 @@ export declare abstract class PlatformAdapter {
|
|
|
70
70
|
*/
|
|
71
71
|
protected getTrackingAdapterScriptAsync(): Promise<string>;
|
|
72
72
|
/**
|
|
73
|
-
* 从 @playcraft/
|
|
74
|
-
* 读取 IIFE
|
|
75
|
-
*
|
|
73
|
+
* 从 @playcraft/ads-tracking 包中提取 tracking 实现代码
|
|
74
|
+
* 读取 IIFE 版本,剥掉最外层的自执行箭头函数包装,
|
|
75
|
+
* 重新包裹为独立 IIFE 并通过 window.PlayCraftTracking 暴露。
|
|
76
76
|
*/
|
|
77
77
|
protected getAdsdkTrackingScriptAsync(): Promise<string>;
|
|
78
78
|
/**
|
|
79
79
|
* 在 HTML 中注入 Tracking 适配器(异步版本)
|
|
80
|
-
* 注入顺序:1.Config → 2.
|
|
80
|
+
* 注入顺序:1.Config → 2.ads-tracking 实现 → 3.Adapter 桥接脚本
|
|
81
81
|
*/
|
|
82
82
|
protected injectTrackingAdapterAsync(html: string): Promise<string>;
|
|
83
83
|
}
|
package/dist/platforms/base.js
CHANGED
|
@@ -6,8 +6,8 @@ import { minifyPatchCode } from '../utils/minify.js';
|
|
|
6
6
|
let cachedMinifiedCTAAdapter = null;
|
|
7
7
|
// 缓存压缩后的 Tracking 适配器代码
|
|
8
8
|
let cachedMinifiedTrackingAdapter = null;
|
|
9
|
-
// 缓存从 @playcraft/
|
|
10
|
-
let
|
|
9
|
+
// 缓存从 @playcraft/ads-tracking 提取的 tracking 实现代码
|
|
10
|
+
let cachedAdsTrackingScript = null;
|
|
11
11
|
// 缓存压缩后的平台脚本
|
|
12
12
|
const platformScriptCache = new Map();
|
|
13
13
|
export class PlatformAdapter {
|
|
@@ -151,54 +151,59 @@ export class PlatformAdapter {
|
|
|
151
151
|
return `<script>${minified}</script>`;
|
|
152
152
|
}
|
|
153
153
|
/**
|
|
154
|
-
* 从 @playcraft/
|
|
155
|
-
* 读取 IIFE
|
|
156
|
-
*
|
|
154
|
+
* 从 @playcraft/ads-tracking 包中提取 tracking 实现代码
|
|
155
|
+
* 读取 IIFE 版本,剥掉最外层的自执行箭头函数包装,
|
|
156
|
+
* 重新包裹为独立 IIFE 并通过 window.PlayCraftTracking 暴露。
|
|
157
157
|
*/
|
|
158
158
|
async getAdsdkTrackingScriptAsync() {
|
|
159
|
-
if (
|
|
160
|
-
return `<script>${
|
|
159
|
+
if (cachedAdsTrackingScript) {
|
|
160
|
+
return `<script>${cachedAdsTrackingScript}</script>`;
|
|
161
161
|
}
|
|
162
162
|
try {
|
|
163
|
-
// 从 @playcraft/
|
|
163
|
+
// 从 @playcraft/ads-tracking 的 IIFE 构建中读取
|
|
164
164
|
const { createRequire } = await import('module');
|
|
165
165
|
const require = createRequire(import.meta.url);
|
|
166
|
-
const
|
|
167
|
-
const iifeCode = await fs.readFile(
|
|
168
|
-
// 提取
|
|
166
|
+
const adsTrackingPath = require.resolve('@playcraft/ads-tracking/dist/iife/index.js');
|
|
167
|
+
const iifeCode = await fs.readFile(adsTrackingPath, 'utf-8');
|
|
168
|
+
// 提取 IIFE 内部实现:ads-tracking 的 IIFE 形如
|
|
169
|
+
// "use strict";\n(() => {\n // src/tracking.ts\n ...\n var tracking = {...};\n})();
|
|
170
|
+
// 需要剥掉最外层 (() => { ... })(); 保留内部代码,
|
|
171
|
+
// 再在末尾挂到 window.PlayCraftTracking 上。
|
|
169
172
|
const startMarker = '// src/tracking.ts';
|
|
170
|
-
const endMarker = 'trackChallengeFailed: _trackChallengeFailed';
|
|
171
173
|
const startIdx = iifeCode.indexOf(startMarker);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
console.warn('[Tracking] 无法从 @playcraft/adsdk 中提取 tracking 代码,使用内置 adapter');
|
|
174
|
+
if (startIdx === -1) {
|
|
175
|
+
console.warn('[Tracking] 无法从 @playcraft/ads-tracking 中提取 tracking 代码,使用内置 adapter');
|
|
175
176
|
return '';
|
|
176
177
|
}
|
|
177
|
-
//
|
|
178
|
-
const
|
|
179
|
-
|
|
178
|
+
// 找到最后一个 "})();" 作为 IIFE 结束
|
|
179
|
+
const endIdx = iifeCode.lastIndexOf('})();');
|
|
180
|
+
if (endIdx === -1 || endIdx <= startIdx) {
|
|
181
|
+
console.warn('[Tracking] 无法定位 @playcraft/ads-tracking IIFE 结尾,使用内置 adapter');
|
|
182
|
+
return '';
|
|
183
|
+
}
|
|
184
|
+
const innerCode = iifeCode.substring(startIdx, endIdx).trimEnd();
|
|
180
185
|
// 包装为独立 IIFE,暴露 window.PlayCraftTracking
|
|
181
|
-
const wrappedCode = `(function(){"use strict";${
|
|
182
|
-
const minified = await minifyPatchCode(wrappedCode, '
|
|
183
|
-
|
|
186
|
+
const wrappedCode = `(function(){"use strict";${innerCode}\nwindow.PlayCraftTracking=tracking;})();`;
|
|
187
|
+
const minified = await minifyPatchCode(wrappedCode, 'ads-tracking-extract');
|
|
188
|
+
cachedAdsTrackingScript = minified;
|
|
184
189
|
return `<script>${minified}</script>`;
|
|
185
190
|
}
|
|
186
191
|
catch (e) {
|
|
187
|
-
console.warn('[Tracking] 无法加载 @playcraft/
|
|
192
|
+
console.warn('[Tracking] 无法加载 @playcraft/ads-tracking:', e.message);
|
|
188
193
|
return '';
|
|
189
194
|
}
|
|
190
195
|
}
|
|
191
196
|
/**
|
|
192
197
|
* 在 HTML 中注入 Tracking 适配器(异步版本)
|
|
193
|
-
* 注入顺序:1.Config → 2.
|
|
198
|
+
* 注入顺序:1.Config → 2.ads-tracking 实现 → 3.Adapter 桥接脚本
|
|
194
199
|
*/
|
|
195
200
|
async injectTrackingAdapterAsync(html) {
|
|
196
201
|
if (this.options.tracking?.enabled === false)
|
|
197
202
|
return html;
|
|
198
203
|
const configScript = this.getTrackingConfigScript();
|
|
199
|
-
const
|
|
204
|
+
const adsTrackingScript = await this.getAdsdkTrackingScriptAsync();
|
|
200
205
|
const trackingScript = await this.getTrackingAdapterScriptAsync();
|
|
201
|
-
const combined = configScript +
|
|
206
|
+
const combined = configScript + adsTrackingScript + trackingScript;
|
|
202
207
|
if (html.includes('</head>')) {
|
|
203
208
|
return html.replace('</head>', `${combined}\n</head>`);
|
|
204
209
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcraft/build",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.38",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
"require": "./dist/types.js",
|
|
17
17
|
"types": "./dist/types.d.ts"
|
|
18
18
|
},
|
|
19
|
+
"./scene-asset-collector": {
|
|
20
|
+
"import": "./dist/analyzers/scene-asset-collector.js",
|
|
21
|
+
"require": "./dist/analyzers/scene-asset-collector.js",
|
|
22
|
+
"types": "./dist/analyzers/scene-asset-collector.d.ts"
|
|
23
|
+
},
|
|
19
24
|
"./dist/*": "./dist/*"
|
|
20
25
|
},
|
|
21
26
|
"files": [
|
|
@@ -30,7 +35,7 @@
|
|
|
30
35
|
"release": "node scripts/release.js"
|
|
31
36
|
},
|
|
32
37
|
"dependencies": {
|
|
33
|
-
"@playcraft/
|
|
38
|
+
"@playcraft/ads-tracking": "^1.0.0",
|
|
34
39
|
"@gltf-transform/core": "^4.3.0",
|
|
35
40
|
"@gltf-transform/extensions": "^4.3.0",
|
|
36
41
|
"@gltf-transform/functions": "^4.3.0",
|
|
@@ -50,7 +55,7 @@
|
|
|
50
55
|
"vite-plugin-singlefile": "^2.3.2"
|
|
51
56
|
},
|
|
52
57
|
"devDependencies": {
|
|
53
|
-
"@playcraft/
|
|
58
|
+
"@playcraft/ads-tracking": "^1.0.0",
|
|
54
59
|
"@types/archiver": "^6.0.4",
|
|
55
60
|
"@types/mime-types": "^2.1.4",
|
|
56
61
|
"@types/node": "^22.19.8",
|
|
@@ -2,8 +2,20 @@
|
|
|
2
2
|
* PlayCraft Tracking Adapter (Bridge Layer)
|
|
3
3
|
*
|
|
4
4
|
* 职责:在正确的生命周期节点调用 window.PlayCraftTracking 的方法。
|
|
5
|
-
* 渠道埋点逻辑由 @playcraft/
|
|
6
|
-
* 如果
|
|
5
|
+
* 渠道埋点逻辑由 @playcraft/ads-tracking 的 tracking 模块提供(构建时自动提取注入)。
|
|
6
|
+
* 如果 ads-tracking 未注入,adapter 的调用会静默忽略。
|
|
7
|
+
*
|
|
8
|
+
* 新 API 对照(@playcraft/ads-tracking v1.0.0):
|
|
9
|
+
* onPlaycraftTrackingInit - SDK 初始化开始
|
|
10
|
+
* onPlaycraftTrackingLoading - 游戏资源加载中
|
|
11
|
+
* onPlaycraftTrackingLoaded - 资源加载完成、主场景就绪
|
|
12
|
+
* onPlaycraftChallengeStart - 游戏挑战开始(首次用户交互)
|
|
13
|
+
* onPlaycraftChallengeProgress - 进度上报 (percent 0-100),内部自动触发里程碑
|
|
14
|
+
* onPlaycraftChallengeSuccess - 游戏挑战成功
|
|
15
|
+
* onPlaycraftChallengeFailed - 游戏挑战失败
|
|
16
|
+
* onPlaycraftChallengeFinish - 游戏结束
|
|
17
|
+
* onPlaycraftInstall - 点击安装
|
|
18
|
+
* onPlaycraftRetry - 重试游戏
|
|
7
19
|
*/
|
|
8
20
|
(function() {
|
|
9
21
|
'use strict';
|
|
@@ -23,28 +35,44 @@
|
|
|
23
35
|
}
|
|
24
36
|
}
|
|
25
37
|
|
|
38
|
+
// ===== 进度上报(兼容旧的 setTotalInteractions + checkInteractionProgress)=====
|
|
39
|
+
// 新包只提供 onPlaycraftChallengeProgress(percent 0-100),里程碑由 SDK 内部处理。
|
|
40
|
+
// 这里维护 total 状态,将 (current/total*100) 转换为百分比上报。
|
|
41
|
+
var _total = totalInteractions > 0 ? totalInteractions : 0;
|
|
42
|
+
function setTotal(n) {
|
|
43
|
+
if (typeof n === 'number' && n > 0) _total = n;
|
|
44
|
+
}
|
|
45
|
+
function reportProgress(current) {
|
|
46
|
+
if (typeof current !== 'number') return;
|
|
47
|
+
var percent;
|
|
48
|
+
if (_total > 0) {
|
|
49
|
+
percent = Math.min(100, Math.max(0, (current / _total) * 100));
|
|
50
|
+
} else {
|
|
51
|
+
// 未设置 total 时,把传入值当作已经是百分比
|
|
52
|
+
percent = Math.min(100, Math.max(0, current));
|
|
53
|
+
}
|
|
54
|
+
call('onPlaycraftChallengeProgress', [percent]);
|
|
55
|
+
}
|
|
56
|
+
|
|
26
57
|
// ===== 自动埋点 =====
|
|
27
58
|
|
|
28
|
-
// 1.
|
|
29
|
-
|
|
30
|
-
call('setTotalInteractions', [totalInteractions]);
|
|
31
|
-
}
|
|
32
|
-
call('onInit', [adNetwork]);
|
|
59
|
+
// 1. onPlaycraftTrackingInit(立即)
|
|
60
|
+
call('onPlaycraftTrackingInit', [adNetwork]);
|
|
33
61
|
|
|
34
|
-
// 2.
|
|
62
|
+
// 2. onPlaycraftTrackingLoading(页面加载完成)
|
|
35
63
|
if (document.readyState === 'complete') {
|
|
36
|
-
setTimeout(function() { call('
|
|
64
|
+
setTimeout(function() { call('onPlaycraftTrackingLoading', [adNetwork]); }, 0);
|
|
37
65
|
} else {
|
|
38
|
-
window.addEventListener('load', function() { call('
|
|
66
|
+
window.addEventListener('load', function() { call('onPlaycraftTrackingLoading', [adNetwork]); });
|
|
39
67
|
}
|
|
40
68
|
|
|
41
|
-
// 2.5
|
|
69
|
+
// 2.5 onPlaycraftTrackingLoaded(游戏完全就绪,可交互)
|
|
42
70
|
// 优先级: playcraft:interactive 事件 > PlayCanvas app.on('start') fallback
|
|
43
71
|
var _interactive = false;
|
|
44
72
|
function triggerInteractive() {
|
|
45
73
|
if (_interactive) return;
|
|
46
74
|
_interactive = true;
|
|
47
|
-
call('
|
|
75
|
+
call('onPlaycraftTrackingLoaded', [adNetwork]);
|
|
48
76
|
}
|
|
49
77
|
// 优先监听游戏主动派发的 playcraft:interactive
|
|
50
78
|
document.addEventListener('playcraft:interactive', triggerInteractive);
|
|
@@ -66,12 +94,12 @@
|
|
|
66
94
|
}
|
|
67
95
|
setTimeout(pollPlayCanvasApp, 0);
|
|
68
96
|
|
|
69
|
-
// 3.
|
|
97
|
+
// 3. onPlaycraftChallengeStart(首次用户交互,只触发一次)
|
|
70
98
|
var _interacted = false;
|
|
71
99
|
function onFirstInteraction() {
|
|
72
100
|
if (_interacted) return;
|
|
73
101
|
_interacted = true;
|
|
74
|
-
call('
|
|
102
|
+
call('onPlaycraftChallengeStart', [adNetwork]);
|
|
75
103
|
document.removeEventListener('touchstart', onFirstInteraction, true);
|
|
76
104
|
document.removeEventListener('mousedown', onFirstInteraction, true);
|
|
77
105
|
document.removeEventListener('pointerdown', onFirstInteraction, true);
|
|
@@ -80,13 +108,13 @@
|
|
|
80
108
|
document.addEventListener('mousedown', onFirstInteraction, true);
|
|
81
109
|
document.addEventListener('pointerdown', onFirstInteraction, true);
|
|
82
110
|
|
|
83
|
-
// 4.
|
|
111
|
+
// 4. onPlaycraftInstall(hook CTA adapter)
|
|
84
112
|
var _ctaAttempts = 0;
|
|
85
113
|
function hookCTA() {
|
|
86
114
|
if (window.PlayCraftCTA && window.PlayCraftCTA.jump) {
|
|
87
115
|
var _orig = window.PlayCraftCTA.jump;
|
|
88
116
|
window.PlayCraftCTA.jump = function(opts) {
|
|
89
|
-
call('
|
|
117
|
+
call('onPlaycraftInstall', [adNetwork]);
|
|
90
118
|
_orig.call(window.PlayCraftCTA, opts);
|
|
91
119
|
};
|
|
92
120
|
if (window.jump2AppStore) window.jump2AppStore = window.PlayCraftCTA.jump;
|
|
@@ -110,21 +138,30 @@
|
|
|
110
138
|
});
|
|
111
139
|
}
|
|
112
140
|
|
|
113
|
-
listenOnce('playcraft:finish', '
|
|
114
|
-
listen('playcraft:success', '
|
|
115
|
-
listen('playcraft:failed', '
|
|
141
|
+
listenOnce('playcraft:finish', 'onPlaycraftChallengeFinish');
|
|
142
|
+
listen('playcraft:success', 'onPlaycraftChallengeSuccess');
|
|
143
|
+
listen('playcraft:failed', 'onPlaycraftChallengeFailed');
|
|
116
144
|
document.addEventListener('playcraft:retry', function() {
|
|
117
145
|
once['playcraft:finish'] = false;
|
|
118
|
-
call('
|
|
146
|
+
call('onPlaycraftRetry', [adNetwork]);
|
|
119
147
|
});
|
|
120
148
|
document.addEventListener('playcraft:setTotal', function(e) {
|
|
121
149
|
var d = e.detail;
|
|
122
150
|
var n = typeof d === 'number' ? d : (d && d.total);
|
|
123
|
-
if (typeof n === 'number')
|
|
151
|
+
if (typeof n === 'number') setTotal(n);
|
|
124
152
|
});
|
|
125
153
|
document.addEventListener('playcraft:progress', function(e) {
|
|
126
154
|
var d = e.detail;
|
|
127
|
-
|
|
128
|
-
if (typeof
|
|
155
|
+
// 兼容两种传参:number(当前值)或 { current, total, percent }
|
|
156
|
+
if (typeof d === 'number') {
|
|
157
|
+
reportProgress(d);
|
|
158
|
+
} else if (d && typeof d === 'object') {
|
|
159
|
+
if (typeof d.total === 'number') setTotal(d.total);
|
|
160
|
+
if (typeof d.percent === 'number') {
|
|
161
|
+
call('onPlaycraftChallengeProgress', [Math.min(100, Math.max(0, d.percent))]);
|
|
162
|
+
} else if (typeof d.current === 'number') {
|
|
163
|
+
reportProgress(d.current);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
129
166
|
});
|
|
130
167
|
})();
|