You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
894 lines
30 KiB
894 lines
30 KiB
import { IBuildTaskOption, BuildHook, IBuildResult } from "../@types";
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const axios = require("axios");
|
|
const { shell } = require("electron");
|
|
|
|
// 添加用于解析和生成 JavaScript 代码的库
|
|
const parser = require("@babel/parser");
|
|
const traverse = require("@babel/traverse").default;
|
|
const generator = require("@babel/generator").default;
|
|
const t = require("@babel/types");
|
|
|
|
const HTTP_URL = "https://miniapp.bilibili.com"; //"http://127.0.0.1:3000"; //https://miniapp.bilibili.com
|
|
|
|
const getUseAsmJs = (L3, B3) => {
|
|
return `function useAsmJs(t, e) {
|
|
return new Promise(function (i, r) {
|
|
var n;
|
|
((n = e),
|
|
new Promise(function (t, e) {
|
|
(function (t) {
|
|
return new Promise(function (e) {
|
|
e("cocos-js/" + t);
|
|
});
|
|
})(n)
|
|
.then(function (i) {
|
|
globalThis.fsUtils.readArrayBuffer(i, function (i, r) {
|
|
i ? e(i) : t(r);
|
|
});
|
|
})
|
|
.catch(function () {});
|
|
}))
|
|
.then(function (e) {
|
|
var i = {};
|
|
return (
|
|
(i.buffer = new ArrayBuffer(33554432)),
|
|
t({
|
|
wasmMemory: i,
|
|
memoryInitializerRequest: { response: e, status: 200 },
|
|
}).then(function (t) {
|
|
(${L3} = t),
|
|
${B3}.forEach(function (t) {
|
|
t(${L3});
|
|
});
|
|
})
|
|
);
|
|
})
|
|
.then(i)
|
|
.catch(r);
|
|
});
|
|
}`;
|
|
};
|
|
|
|
const getUseWebassembly = (L3, B3) => {
|
|
return `function useWebAssembly(t, e) {
|
|
return new Promise(function (i, n) {
|
|
var r = function (t) {
|
|
return "[Spine]: Spine wasm load failed: " + t;
|
|
};
|
|
t({
|
|
instantiateWasm: function (t, i) {
|
|
(function (t, e) {
|
|
return ((i = t),
|
|
new Promise(function (t) {
|
|
t("cocos-js/" + i);
|
|
})).then(function (t) {
|
|
return WebAssembly.instantiate(t, e);
|
|
});
|
|
var i;
|
|
})(e, t)
|
|
.then(function (t) {
|
|
i(t.instance, t.module);
|
|
})
|
|
.catch(function (t) {
|
|
return n(r(t));
|
|
});
|
|
},
|
|
})
|
|
.then(function (t) {
|
|
(${L3} = t),
|
|
${B3}.forEach(function (t) {
|
|
t(${L3});
|
|
});
|
|
})
|
|
.then(i)
|
|
.catch(function (t) {
|
|
return n(r(t));
|
|
});
|
|
});
|
|
}`;
|
|
};
|
|
|
|
function getOnPostInfrastructureInitDelegateAddFuncStr(code, isSubPackageWasm) {
|
|
const startIndex = code.indexOf("Spine wasm load failed");
|
|
console.log("开始找 Spine wasm load failed");
|
|
if (startIndex === -1) {
|
|
console.log("未找到 Spine wasm load failed。");
|
|
} else {
|
|
// 从 startIndex 开始向前搜索找到第一个 ;
|
|
const openBraceIndex = code.lastIndexOf(";", startIndex);
|
|
if (openBraceIndex === -1) {
|
|
console.log("未找到方法的起始 ;。");
|
|
} else {
|
|
const lastSemicolonIndex = code.lastIndexOf(";", openBraceIndex - 1);
|
|
if (lastSemicolonIndex == -1) {
|
|
console.log("--------");
|
|
} else {
|
|
const funIndex = code.indexOf(
|
|
"onPostInfrastructureInitDelegate.add",
|
|
lastSemicolonIndex
|
|
);
|
|
if (funIndex === -1) {
|
|
console.log("未找到 onPostInfrastructureInitDelegate.add。");
|
|
} else {
|
|
// 从 startIndex 开始向后搜索找到第一个 catch
|
|
const pushIndex = code.indexOf(".push", funIndex);
|
|
if (pushIndex === -1) {
|
|
console.log("未找 .pushIndex 语法。");
|
|
} else {
|
|
const commaIndex = code.lastIndexOf(",", pushIndex + 1);
|
|
if (commaIndex === -1) {
|
|
console.log("未找到结束标志 ,");
|
|
} else {
|
|
// 提取起始位置和结束位置之间的子字符串
|
|
const methodContent = code.substring(
|
|
lastSemicolonIndex + 1,
|
|
commaIndex
|
|
);
|
|
console.log("方法内容:", methodContent);
|
|
return methodContent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
const EXTENSION_VERSION = "1.0.3";
|
|
|
|
// 复制文件的函数
|
|
function copyFiles(srcDir, destDir) {
|
|
// 确保目标目录存在
|
|
if (!fs.existsSync(destDir)) {
|
|
fs.mkdirSync(destDir, { recursive: true });
|
|
}
|
|
|
|
// 读取源目录下的所有文件
|
|
const files = fs.readdirSync(srcDir);
|
|
console.log("copyFiles", files);
|
|
|
|
for (const file of files) {
|
|
const srcFile = path.join(srcDir, file);
|
|
const destFile = path.join(destDir, file);
|
|
fs.copyFileSync(srcFile, destFile);
|
|
}
|
|
}
|
|
|
|
// 递归复制目录的函数
|
|
function copyDirectory(srcDir, destDir) {
|
|
// 确保目标目录存在
|
|
if (!fs.existsSync(destDir)) {
|
|
fs.mkdirSync(destDir, { recursive: true });
|
|
}
|
|
|
|
// 读取源目录下的所有文件和目录
|
|
const items = fs.readdirSync(srcDir);
|
|
items.forEach((item) => {
|
|
const srcPath = path.join(srcDir, item);
|
|
const destPath = path.join(destDir, item);
|
|
const stat = fs.statSync(srcPath);
|
|
|
|
if (stat.isDirectory()) {
|
|
// 如果是目录,则递归复制
|
|
copyDirectory(srcPath, destPath);
|
|
} else {
|
|
// 如果是文件,则直接复制
|
|
fs.copyFileSync(srcPath, destPath);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 写文件
|
|
function writeFile(localFilePath, response) {
|
|
fs.writeFileSync(localFilePath, response.data, "utf8");
|
|
}
|
|
|
|
// 清空目录的函数
|
|
function clearDirectory(directory) {
|
|
if (fs.existsSync(directory)) {
|
|
const files = fs.readdirSync(directory);
|
|
files.forEach((file) => {
|
|
const filePath = path.join(directory, file);
|
|
const stat = fs.statSync(filePath);
|
|
|
|
if (stat.isDirectory()) {
|
|
// 如果是目录,则递归删除
|
|
clearDirectory(filePath);
|
|
} else {
|
|
// 如果是文件,则直接删除
|
|
fs.unlinkSync(filePath);
|
|
}
|
|
});
|
|
// 最后删除目录本身
|
|
fs.rmdirSync(directory);
|
|
}
|
|
}
|
|
|
|
function compareVersions(version1, version2) {
|
|
if (!version1 || !version2) {
|
|
return 0;
|
|
}
|
|
|
|
const v1 = version1.split(".").map(Number);
|
|
const v2 = version2.split(".").map(Number);
|
|
|
|
for (let i = 0; i < Math.max(v1.length, v2.length); i++) {
|
|
const num1 = v1[i] || 0; // 如果不存在,默认为 0
|
|
const num2 = v2[i] || 0; // 如果不存在,默认为 0
|
|
|
|
if (num1 > num2) return 1; // version1 > version2
|
|
if (num2 > num1) return -1; // version2 > version1
|
|
}
|
|
|
|
return 0; // 版本号相同
|
|
}
|
|
|
|
// 替换 System.register 的模块定义
|
|
function replaceSystemRegister(sourceCode, moduleName, replacementCode) {
|
|
const ast = parser.parse(sourceCode, {
|
|
sourceType: "module",
|
|
});
|
|
|
|
traverse(ast, {
|
|
CallExpression(path) {
|
|
if (
|
|
path.node.callee.type === "MemberExpression" &&
|
|
path.node.callee.object.name === "System" &&
|
|
path.node.callee.property.name === "register" &&
|
|
path.node.arguments.length === 3 &&
|
|
path.node.arguments[0].type === "StringLiteral" &&
|
|
path.node.arguments[0].value === moduleName
|
|
) {
|
|
console.log("找到 System.register 调用: ", moduleName);
|
|
|
|
// 替换整个 System.register 调用
|
|
const newRegisterAst = parser.parse(replacementCode, {
|
|
sourceType: "module",
|
|
}).program.body[0].expression;
|
|
|
|
path.replaceWith(newRegisterAst);
|
|
|
|
// 如果只需要替换第一个匹配,可以调用 path.stop();
|
|
path.stop();
|
|
}
|
|
},
|
|
});
|
|
|
|
return generator(ast, { concise: true }).code;
|
|
}
|
|
|
|
function replaceSpineWasm(originalCode) {
|
|
const ast = parser.parse(originalCode, {
|
|
sourceType: "module",
|
|
});
|
|
|
|
console.log("start parse onPostInfrastructureInitDelegateFunc");
|
|
const onPostInfrastructureInitDelegateFunc = `
|
|
(function (t) {
|
|
var platform = wx.getSystemInfoSync()._platform || wx.getSystemInfoSync().platform || "";
|
|
if (platform.toLowerCase() == "ios") {
|
|
return useAsmJs(t[0].default, t[1].default);
|
|
}
|
|
return useWebAssembly(t[2].default, t[3].default);
|
|
})
|
|
`;
|
|
|
|
const funcExprAst = parser.parse(onPostInfrastructureInitDelegateFunc, {
|
|
sourceType: "module",
|
|
plugins: ["typescript"],
|
|
}).program.body[0].expression;
|
|
|
|
// 遍历 AST 进行修改
|
|
traverse(ast, {
|
|
CallExpression(path) {
|
|
// 检查我们是否在处理Promise.all的调用
|
|
if (
|
|
path.node.callee.type === "MemberExpression" &&
|
|
path.node.callee.property.name === "then"
|
|
) {
|
|
console.log("Found 'then'");
|
|
|
|
// 检查是否为Promise.all的调用结果
|
|
if (
|
|
path.node.callee.object.type === "CallExpression" &&
|
|
path.node.callee.object.callee.type === "MemberExpression" &&
|
|
path.node.callee.object.callee.object.name === "Promise" &&
|
|
path.node.callee.object.callee.property.name === "all"
|
|
) {
|
|
console.log("Found 'Promise.all'");
|
|
|
|
// 确保只有一个参数,并且这个参数是数组表达式
|
|
if (
|
|
path.node.callee.object.arguments.length === 1 &&
|
|
t.isArrayExpression(path.node.callee.object.arguments[0])
|
|
) {
|
|
console.log("Arguments length is 1 and is an Array");
|
|
|
|
// 检查数组元素
|
|
if (
|
|
path.node.callee.object.arguments[0].elements.every(
|
|
(element) =>
|
|
t.isCallExpression(element) &&
|
|
t.isMemberExpression(element.callee) &&
|
|
t.isIdentifier(element.callee.object, { name: "e" }) &&
|
|
t.isIdentifier(element.callee.property, { name: "import" }) &&
|
|
t.isStringLiteral(element.arguments[0]) &&
|
|
(element.arguments[0].value.startsWith(
|
|
"external:emscripten/spine/spine"
|
|
) ||
|
|
element.arguments[0].value.startsWith("./spine"))
|
|
)
|
|
) {
|
|
console.log("All imports match criteria");
|
|
console.log("path.get('arguments.0')", path.get("arguments.0"));
|
|
path.get("arguments.0").replaceWith(funcExprAst);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
});
|
|
|
|
// 将函数代码注入 AST 的末尾
|
|
// ast.program.body.push(parser.parse(asmJsFunc, { sourceType: "module" }).program.body[0]);
|
|
// ast.program.body.push(parser.parse(webassemblyFunc, { sourceType: "module" }).program.body[0]);
|
|
|
|
// 生成新的代码
|
|
// const { code } = generator(ast);
|
|
return generator(ast, { concise: true }).code;
|
|
}
|
|
|
|
function insertFuncs(originalCode) {
|
|
const ast = parser.parse(originalCode, {
|
|
sourceType: "module",
|
|
});
|
|
|
|
// console.log('insertFuncs originalCode: ', originalCode);
|
|
|
|
// 从原始代码中提取变量名
|
|
const varDeclaration = originalCode.split(";")[0]; // 取第一个分号前的部分
|
|
const varNames = varDeclaration
|
|
.replace("var ", "")
|
|
.split(",")
|
|
.reduce((acc, item) => {
|
|
let [key, value] = item.split("=");
|
|
if (value) {
|
|
console.log("value", value);
|
|
if (value.indexOf("null") != -1) {
|
|
acc.Ont = key.trim();
|
|
} else if (value.indexOf("[]") != -1) {
|
|
acc.Mnt = key.trim();
|
|
}
|
|
}
|
|
return acc;
|
|
}, {});
|
|
|
|
// 注入 getUseAsmJs 和 getUseWebassembly 函数
|
|
const asmJsFunc = getUseAsmJs(varNames.Ont, varNames.Mnt);
|
|
const webassemblyFunc = getUseWebassembly(varNames.Ont, varNames.Mnt);
|
|
// 解析单个函数定义为 AST 节点
|
|
const asmJsFuncAst = parser.parse(asmJsFunc, { sourceType: "module" }).program
|
|
.body[0];
|
|
const webassemblyFuncAst = parser.parse(webassemblyFunc, {
|
|
sourceType: "module",
|
|
}).program.body[0];
|
|
|
|
// 遍历 AST 进行修改
|
|
traverse(ast, {
|
|
CallExpression(path) {
|
|
// 检查是否为 .add 调用并且前面有 .onPostInfrastructureInitDelegate
|
|
if (
|
|
path.node.callee.type === "MemberExpression" &&
|
|
path.node.callee.property.name === "add" &&
|
|
path.node.callee.object.type === "MemberExpression" &&
|
|
path.node.callee.object.property.name ===
|
|
"onPostInfrastructureInitDelegate"
|
|
) {
|
|
// 在此调用前插入 asmJsFunc 和 webassemblyFunc
|
|
path.insertBefore(asmJsFuncAst);
|
|
path.insertBefore(webassemblyFuncAst);
|
|
}
|
|
},
|
|
});
|
|
return generator(ast, { concise: true }).code;
|
|
}
|
|
|
|
|
|
|
|
// 递归函数,用于遍历目录
|
|
function renameWebAdapterFiles(dirPath) {
|
|
fs.readdir(dirPath, (err, files) => {
|
|
if (err) {
|
|
console.error('Error reading directory:', err);
|
|
return;
|
|
}
|
|
|
|
files.forEach(file => {
|
|
const filePath = path.join(dirPath, file);
|
|
const stat = fs.statSync(filePath);
|
|
|
|
if (stat.isDirectory()) {
|
|
// 如果是目录,递归调用
|
|
renameWebAdapterFiles(filePath);
|
|
} else if (file.startsWith('web-adapter.') && file.endsWith('.js')) {
|
|
// 匹配 web-adapter.xxxx.js 文件
|
|
const newFilePath = path.join(dirPath, 'web-adapter.js');
|
|
|
|
// 检查新文件名是否已存在,如果存在则跳过或处理冲突
|
|
if (files.includes('web-adapter.js')) {
|
|
console.log(`${newFilePath} already exists, skipping...`);
|
|
} else {
|
|
fs.rename(filePath, newFilePath, err => {
|
|
if (err) {
|
|
console.error('Error renaming file:', err);
|
|
} else {
|
|
console.log(`Renamed ${filePath} to ${newFilePath}`);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
interface IOptions {
|
|
platform: any;
|
|
webTestOption: any;
|
|
remoteAddress: string;
|
|
enterCocos: string;
|
|
selectTest: string;
|
|
objectTest: {
|
|
number: number;
|
|
string: string;
|
|
boolean: boolean;
|
|
};
|
|
arrayTest: [number, string, boolean];
|
|
isBiliGame: boolean;
|
|
}
|
|
|
|
const PACKAGE_NAME = "biligame-builder";
|
|
|
|
interface ITaskOptions extends IBuildTaskOption {
|
|
packages: {
|
|
"biligame-builder": IOptions;
|
|
};
|
|
}
|
|
|
|
function log(...arg: any[]) {
|
|
return console.log(`[${PACKAGE_NAME}] `, ...arg);
|
|
}
|
|
|
|
let allAssets = [];
|
|
|
|
export const throwError: BuildHook.throwError = true;
|
|
|
|
export const load: BuildHook.load = async function () {
|
|
console.log(`[${PACKAGE_NAME}] Load cocos plugin example in builder.`);
|
|
allAssets = await Editor.Message.request("asset-db", "query-assets");
|
|
};
|
|
|
|
export const onBeforeBuild: BuildHook.onBeforeBuild = async function (
|
|
options: ITaskOptions,
|
|
result: IBuildResult
|
|
) {
|
|
const pkgOptions = options.packages[PACKAGE_NAME];
|
|
if (options.platform == "wechatgame" && pkgOptions.isBiliGame) {
|
|
// Todo some thing
|
|
// log(`${PACKAGE_NAME}.webTestOption`, 'onBeforeBuild');
|
|
|
|
console.log("options", options);
|
|
|
|
let res = await axios.get(
|
|
`${HTTP_URL}/game-issues/api/issue/getAdaptation/getVersion`
|
|
);
|
|
|
|
const orgVersion = res.data.data.version;
|
|
const currVersion = EXTENSION_VERSION;
|
|
|
|
if (compareVersions(orgVersion, currVersion) == 1) {
|
|
return new Promise((resolve, reject) => {
|
|
// 显示一个对话框提示用户
|
|
Editor.Dialog.info("biligame-builder有新更新,是否前往下载最新版本?", {
|
|
buttons: ["继续构建", "停止构建,并前往官网"],
|
|
title: "插件更新提醒",
|
|
}).then((result) => {
|
|
if (result.response === 0) {
|
|
// 用户选择继续,解析 Promise 以继续构建
|
|
resolve();
|
|
} else {
|
|
// 打开默认浏览器并导航到指定URL
|
|
shell.openExternal(
|
|
"https://miniapp.bilibili.com/small-game-doc/guide/compatibility/"
|
|
);
|
|
// open('https://miniapp.bilibili.com/small-game-doc/guide/compatibility/');
|
|
// 用户选择取消,拒绝 Promise 以阻止构建
|
|
reject(new Error("用户取消操作,构建已停止。"));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
console.log("res", res);
|
|
}
|
|
};
|
|
|
|
export const onBeforeCompressSettings: BuildHook.onBeforeCompressSettings =
|
|
async function (options: ITaskOptions, result: IBuildResult) {
|
|
// const pkgOptions = options.packages[PACKAGE_NAME];
|
|
// if (pkgOptions.webTestOption) {
|
|
// console.debug('webTestOption', true);
|
|
// }
|
|
// Todo some thing
|
|
console.debug("get settings test", result.settings);
|
|
};
|
|
|
|
export const onAfterCompressSettings: BuildHook.onAfterCompressSettings =
|
|
async function (options: ITaskOptions, result: IBuildResult) {
|
|
// Todo some thing
|
|
console.log("webTestOption", "onAfterCompressSettings");
|
|
};
|
|
|
|
export const onAfterBuild: BuildHook.onAfterBuild = async function (
|
|
options: ITaskOptions,
|
|
result: IBuildResult
|
|
) {
|
|
// change the uuid to test
|
|
let orgVersion = Editor.App.version;
|
|
let version = Editor.App.version.split(".").join("");
|
|
|
|
const pkgOptions = options.packages[PACKAGE_NAME];
|
|
console.log("options.packages:", JSON.stringify(options.packages));
|
|
if (options.platform == "wechatgame" && pkgOptions.isBiliGame) {
|
|
console.log("正在构建bilibili小游戏~");
|
|
try {
|
|
console.log("开始拉取适配文件");
|
|
// `https://miniapp.bilibili.com/game-issues/api/issue/getAdaptation?version=${orgVersion.slice(
|
|
let res = await axios.get(
|
|
`${HTTP_URL}/game-issues/api/issue/getAdaptation?version=${orgVersion.slice(
|
|
0,
|
|
3
|
|
)}`
|
|
);
|
|
let newRequireStatements = "";
|
|
const data = res.data.data;
|
|
console.log("拉取到了适配文件", data.files);
|
|
if (data?.files?.length) {
|
|
const extensionDir = __dirname;
|
|
const srcDir = path.join(extensionDir, "../static/adapters/download");
|
|
|
|
const biligamePath = path.join(result.dest, "../biligame");
|
|
|
|
// 创建新目录
|
|
if (!fs.existsSync(biligamePath)) {
|
|
console.log("创建biligame目录");
|
|
fs.mkdirSync(biligamePath, { recursive: true });
|
|
}
|
|
|
|
clearDirectory(srcDir);
|
|
if (!fs.existsSync(srcDir)) {
|
|
console.log("创建适配文件目录");
|
|
fs.mkdirSync(srcDir, { recursive: true });
|
|
}
|
|
|
|
clearDirectory(biligamePath);
|
|
copyDirectory(result.dest, biligamePath);
|
|
|
|
for (let i = 0; i < data?.files?.length; i++) {
|
|
const fileName = Object.keys(data.files[i])[0];
|
|
console.log("fileName", fileName);
|
|
|
|
newRequireStatements =
|
|
newRequireStatements + `require('./${fileName}');\n`;
|
|
|
|
const localFilePath = path.join(biligamePath, `${fileName}`);
|
|
|
|
const fileRes = await axios({
|
|
method: "get",
|
|
url: data.files[i][fileName],
|
|
responseType: "stream",
|
|
});
|
|
console.log("fileRes", fileRes);
|
|
|
|
// 把适配文件,写入biligame目录
|
|
writeFile(localFilePath, fileRes);
|
|
}
|
|
|
|
renameWebAdapterFiles(biligamePath);
|
|
console.log("写入适配文件成功!");
|
|
|
|
// if (version.length < 3) {
|
|
// version = version + "";
|
|
// }
|
|
|
|
// let versionNum = parseInt(version);
|
|
const destDir = path.join(biligamePath, "."); // 'result.dest' 是构建目录的路径
|
|
|
|
// 修改 game.js
|
|
const gameJsPath = path.join(destDir, "game.js");
|
|
let gameJsContent = fs.readFileSync(gameJsPath, "utf8");
|
|
|
|
if (compareVersions(orgVersion, "3.6.0") == -1) {
|
|
console.log("引擎版本小于 3.6.x, 使用第1套适配方案");
|
|
gameJsContent = gameJsContent.replace(
|
|
"require('./libs/wrapper/builtin/index')",
|
|
""
|
|
);
|
|
} else {
|
|
console.log("引擎版本大于等于 3.6.x, 使用第2套适配方案");
|
|
|
|
let start = gameJsContent.indexOf("require('./web-adapter");
|
|
if (start !== -1) {
|
|
// 向前找到前一个分号
|
|
let prevSemicolon = gameJsContent.lastIndexOf(';', start) + 1;
|
|
// 向后找到后一个分号
|
|
let nextSemicolon = gameJsContent.indexOf(';', start) + 1;
|
|
if (prevSemicolon !== -1 && nextSemicolon !== -1) {
|
|
// 删除这个区间的内容
|
|
gameJsContent = gameJsContent.slice(0, prevSemicolon) + gameJsContent.slice(nextSemicolon);
|
|
}
|
|
}
|
|
|
|
console.log("gameJsContent", gameJsContent);
|
|
}
|
|
|
|
// 下方添加launchSuccess();
|
|
gameJsContent += "\nif(bl){bl.launchSuccess()};";
|
|
|
|
// 将新的 require 语句添加到 game.js 的开头
|
|
gameJsContent = newRequireStatements + gameJsContent;
|
|
|
|
// 将修改后的内容写回 game.js
|
|
fs.writeFileSync(gameJsPath, gameJsContent, "utf8");
|
|
|
|
console.log("game.js适配成功");
|
|
|
|
const gameJsonPath = path.join(destDir, "game.json");
|
|
let gameJsonContent = fs.readFileSync(gameJsonPath, "utf8");
|
|
let gameJsonFormat = JSON.parse(gameJsonContent);
|
|
if (!gameJsonFormat["appId"]) {
|
|
console.log("没有appId,正在写入中~");
|
|
gameJsonFormat["appId"] =
|
|
pkgOptions["biliGameAppId"] || options.packages["wechatgame"].appid;
|
|
gameJsonFormat["version"] = pkgOptions["biliGameVersion"] || "1.0.0";
|
|
let newGameJsonContent = JSON.stringify(gameJsonFormat);
|
|
fs.writeFileSync(gameJsonPath, newGameJsonContent, "utf8");
|
|
}
|
|
|
|
console.log("game.json适配成功!");
|
|
|
|
if (
|
|
compareVersions(orgVersion, "3.8.0") == 1 ||
|
|
compareVersions(orgVersion, "3.8.0") == 0
|
|
) {
|
|
if (compareVersions(orgVersion, "3.8.3") == -1) {
|
|
console.log("版本大于等于3.8.0并且小于3.8.3,开始对 spine wasm 进行适配~");
|
|
if (options.packages["wechatgame"].wasmSubpackage) {
|
|
console.log("已开启 wasm 分包策略,开始处理~");
|
|
|
|
// 读取 spine.asm.js 和 spine.js.mem 的内容
|
|
let spineAsmModulePath = path.join(
|
|
extensionDir,
|
|
"../static/spine/cocos3.8-spine-wasm-subpackage-open/spine.asm.js"
|
|
);
|
|
let spineMemModulePath = path.join(
|
|
extensionDir,
|
|
"../static/spine/cocos3.8-spine-wasm-subpackage-open/spine.js.mem.js"
|
|
);
|
|
|
|
if (compareVersions(orgVersion, "3.8.2") == -1) {
|
|
console.log("版本低于 3.8.2, 不支持wasm分包, 退出适配");
|
|
return;
|
|
}
|
|
|
|
const spineAsmCode = fs.readFileSync(spineAsmModulePath, "utf8");
|
|
const spineMemCode = fs.readFileSync(spineMemModulePath, "utf8");
|
|
|
|
// 替换 game.js 中的相应模块定义
|
|
const gameJsPath = path.join(destDir, "cocos-js/chunks/game.js");
|
|
let gameJsContent = fs.readFileSync(gameJsPath, "utf8");
|
|
|
|
console.log("正在构建chunks/game.js语法树~");
|
|
gameJsContent = replaceSystemRegister(
|
|
gameJsContent,
|
|
"external:emscripten/spine/spine.asm.js",
|
|
spineAsmCode
|
|
);
|
|
gameJsContent = replaceSystemRegister(
|
|
gameJsContent,
|
|
"external:emscripten/spine/spine.js.mem",
|
|
spineMemCode
|
|
);
|
|
|
|
// 将修改后的 game.js 写回文件
|
|
fs.writeFileSync(gameJsPath, gameJsContent, "utf8");
|
|
|
|
console.log("已生成新的chunks/game.js~");
|
|
console.log("spine.asm.js 和 spine.js.mem 替换完成");
|
|
|
|
console.log("正在拷贝 spine.js.mem.bin ~");
|
|
|
|
const assetPath = path.join(destDir, "cocos-js/assets");
|
|
const spineMemBin = path.join(
|
|
extensionDir,
|
|
"../static/spine/cocos3.8-spine-wasm-subpackage-open/spine.js.mem.bin"
|
|
);
|
|
const targetFile = path.join(assetPath, "spine.js.mem.bin");
|
|
|
|
// 拷贝文件
|
|
fs.copyFileSync(spineMemBin, targetFile);
|
|
console.log("拷贝 spine.js.mem.bin 成功~");
|
|
|
|
console.log("开始处理cc文件~");
|
|
// 指定目录路径
|
|
const cocosJSPath = path.join(destDir, "cocos-js");
|
|
|
|
// 正则表达式匹配可能的 JavaScript 文件
|
|
const regex = /^cc(\..*)?\.js$/;
|
|
|
|
let ccPath = "";
|
|
// 读取目录内容
|
|
fs.readdir(cocosJSPath, (err, files) => {
|
|
if (err) {
|
|
return console.error("Unable to scan directory: " + err);
|
|
}
|
|
|
|
// 过滤出符合条件的文件
|
|
const filteredFiles = files.filter((file) => regex.test(file));
|
|
|
|
// 处理每个找到的文件
|
|
filteredFiles.forEach((file) => {
|
|
ccPath = path.join(cocosJSPath, file);
|
|
// console.log('Found file:', ccPath);
|
|
let ccFileCode = fs.readFileSync(ccPath, "utf8");
|
|
|
|
// console.log("originCode:", ccFileCode);
|
|
|
|
const funcStr = getOnPostInfrastructureInitDelegateAddFuncStr(
|
|
ccFileCode,
|
|
true
|
|
);
|
|
|
|
let newCode = replaceSpineWasm(funcStr);
|
|
newCode = insertFuncs(newCode);
|
|
|
|
if (newCode.endsWith(";")) {
|
|
newCode = newCode.slice(0, newCode.length - 1);
|
|
}
|
|
|
|
// fs.copyFileSync(ccPath, path.join(cocosJSPath, "cc.old.js"));
|
|
|
|
ccFileCode = ccFileCode.replace(funcStr, newCode);
|
|
// console.log("newCode", newCode);
|
|
// console.log("newCCFileCode", ccFileCode);
|
|
|
|
fs.writeFileSync(ccPath, ccFileCode, "utf8");
|
|
|
|
console.log("重写cc.js 成功!");
|
|
});
|
|
|
|
if (filteredFiles.length === 0) {
|
|
console.log("No matching JavaScript files found.");
|
|
}
|
|
});
|
|
} else {
|
|
console.log("未开启 wasm 分包策略,开始处理~");
|
|
|
|
let spineAsmModulePath = path.join(
|
|
extensionDir,
|
|
"../static/spine/cocos3.8-spine-wasm-subpackage-close/spine.asm.js"
|
|
);
|
|
let spineJsModulePath = path.join(
|
|
extensionDir,
|
|
"../static/spine/cocos3.8-spine-wasm-subpackage-close/spine.js.js"
|
|
);
|
|
|
|
if (compareVersions(orgVersion, "3.8.2") == -1) {
|
|
console.log("版本低于 3.8.2, 选用3.8.1适配文件");
|
|
spineAsmModulePath = path.join(
|
|
extensionDir,
|
|
"../static/spine/cocos3.8.1/wasm-subpackage-close/spine.asm.js"
|
|
);
|
|
spineJsModulePath = path.join(
|
|
extensionDir,
|
|
"../static/spine/cocos3.8.1/wasm-subpackage-close/spine.js.js"
|
|
);
|
|
}
|
|
|
|
const spineAsmCode = fs.readFileSync(spineAsmModulePath, "utf8");
|
|
const spineJsCode = fs.readFileSync(spineJsModulePath, "utf8");
|
|
|
|
const cocosJSPath = path.join(destDir, "cocos-js");
|
|
|
|
console.log("正在拷贝 spine.js.mem.bin ~");
|
|
const assetPath = path.join(destDir, "cocos-js/assets");
|
|
let spineMemBin = path.join(
|
|
extensionDir,
|
|
"../static/spine/cocos3.8-spine-wasm-subpackage-close/spine.js.mem.bin"
|
|
);
|
|
|
|
|
|
if (compareVersions(orgVersion, "3.8.2") == -1) {
|
|
console.log("版本低于 3.8.2, 选用3.8.1 spineMemBin 适配文件");
|
|
spineMemBin = path.join(
|
|
extensionDir,
|
|
"../static/spine/cocos3.8.1/wasm-subpackage-close/spine.js.mem.bin"
|
|
);
|
|
}
|
|
|
|
const targetFile = path.join(assetPath, "spine.js.mem.bin");
|
|
fs.copyFileSync(spineMemBin, targetFile);
|
|
|
|
fs.readdir(cocosJSPath, (err, files) => {
|
|
files.forEach((file) => {
|
|
let filePath = path.join(cocosJSPath, file);
|
|
if (file.startsWith("spine.asm-")) {
|
|
console.log("正在重写 spine.asm.js~");
|
|
fs.writeFileSync(filePath, spineAsmCode, "utf8");
|
|
}
|
|
if (file.startsWith("spine.js-")) {
|
|
console.log("正在重写 spine.js.js~");
|
|
fs.writeFileSync(filePath, spineJsCode, "utf8");
|
|
}
|
|
if (file.startsWith("_virtual_cc")) {
|
|
let ccFileCode = fs.readFileSync(filePath, "utf8");
|
|
const funcStr =
|
|
getOnPostInfrastructureInitDelegateAddFuncStr(
|
|
ccFileCode,
|
|
false
|
|
);
|
|
|
|
let newCode = replaceSpineWasm(funcStr);
|
|
newCode = insertFuncs(newCode);
|
|
|
|
if (newCode.endsWith(";")) {
|
|
newCode = newCode.slice(0, newCode.length - 1);
|
|
}
|
|
|
|
ccFileCode = ccFileCode.replace(funcStr, newCode);
|
|
fs.writeFileSync(filePath, ccFileCode, "utf8");
|
|
|
|
console.log("重新_virtual_cc.js 成功!");
|
|
}
|
|
});
|
|
});
|
|
}
|
|
} else {
|
|
console.log("版本大于等于3.8.3,开始适配");
|
|
|
|
}
|
|
}
|
|
|
|
console.log("bilibili小游戏构建成功!");
|
|
}
|
|
} catch (error) {
|
|
console.log("文件下载失败:", error);
|
|
}
|
|
}
|
|
// test onError hook
|
|
// throw new Error('Test onError');
|
|
};
|
|
|
|
export const unload: BuildHook.unload = async function () {
|
|
console.log(`[${PACKAGE_NAME}] Unload cocos plugin example in builder.`);
|
|
};
|
|
|
|
export const onError: BuildHook.onError = async function (options, result) {
|
|
// Todo some thing
|
|
console.warn(`${PACKAGE_NAME} run onError`);
|
|
};
|
|
|
|
export const onBeforeMake: BuildHook.onBeforeMake = async function (
|
|
root,
|
|
options
|
|
) {
|
|
console.log(`onBeforeMake: root: ${root}, options: ${options}`);
|
|
};
|
|
|
|
export const onAfterMake: BuildHook.onAfterMake = async function (
|
|
root,
|
|
options
|
|
) {
|
|
console.log(`onAfterMake: root: ${root}, options: ${options}`);
|
|
};
|
|
|