356 lines
17 KiB
JavaScript
356 lines
17 KiB
JavaScript
// fakeLog 函数,使用方法:将本函数放在主函数前,调用时请务必使用await,否则可能出现v8白框报错
|
||
//在js开头处伪造该js结束运行的日志信息,如 await fakeLog("js脚本", true, true, 0);
|
||
//在js结尾处伪造该js开始运行的日志信息,如 await fakeLog("js脚本", true, false, 2333);(可以不加)
|
||
//duration项目仅在伪造结束信息时有效,且无实际作用,可以任意填写,当你需要在日志中输出特定值时才需要,单位为毫秒
|
||
//在调用地图追踪前伪造该地图追踪开始运行的日志信息,如 await fakeLog(`地图追踪.json`, false, true, 0);
|
||
//在调用地图追踪后伪造该地图追踪结束运行的日志信息,如 await fakeLog(`地图追踪.json`, false, false, 0);
|
||
//如此便可以在js运行过程中伪造地图追踪的日志信息,可以在日志分析等中查看
|
||
|
||
async function fakeLog(name, isJs, isStart, duration) {
|
||
await sleep(10);
|
||
const currentTime = Date.now();
|
||
// 参数检查
|
||
if (typeof name !== 'string') {
|
||
log.error("参数 'name' 必须是字符串类型!");
|
||
return;
|
||
}
|
||
if (typeof isJs !== 'boolean') {
|
||
log.error("参数 'isJs' 必须是布尔型!");
|
||
return;
|
||
}
|
||
if (typeof isStart !== 'boolean') {
|
||
log.error("参数 'isStart' 必须是布尔型!");
|
||
return;
|
||
}
|
||
if (typeof currentTime !== 'number' || !Number.isInteger(currentTime)) {
|
||
log.error("参数 'currentTime' 必须是整数!");
|
||
return;
|
||
}
|
||
if (typeof duration !== 'number' || !Number.isInteger(duration)) {
|
||
log.error("参数 'duration' 必须是整数!");
|
||
return;
|
||
}
|
||
|
||
// 将 currentTime 转换为 Date 对象并格式化为 HH:mm:ss.sss
|
||
const date = new Date(currentTime);
|
||
const hours = String(date.getHours()).padStart(2, '0');
|
||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||
const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
|
||
const formattedTime = `${hours}:${minutes}:${seconds}.${milliseconds}`;
|
||
|
||
// 将 duration 转换为分钟和秒,并保留三位小数
|
||
const durationInSeconds = duration / 1000; // 转换为秒
|
||
const durationMinutes = Math.floor(durationInSeconds / 60);
|
||
const durationSeconds = (durationInSeconds % 60).toFixed(3); // 保留三位小数
|
||
|
||
// 使用四个独立的 if 语句处理四种情况
|
||
if (isJs && isStart) {
|
||
// 处理 isJs = true 且 isStart = true 的情况
|
||
const logMessage = `正在伪造js开始的日志记录\n\n` +
|
||
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
|
||
`------------------------------\n\n` +
|
||
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
|
||
`→ 开始执行JS脚本: "${name}"`;
|
||
log.debug(logMessage);
|
||
}
|
||
if (isJs && !isStart) {
|
||
// 处理 isJs = true 且 isStart = false 的情况
|
||
const logMessage = `正在伪造js结束的日志记录\n\n` +
|
||
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
|
||
`→ 脚本执行结束: "${name}", 耗时: ${durationMinutes}分${durationSeconds}秒\n\n` +
|
||
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
|
||
`------------------------------`;
|
||
log.debug(logMessage);
|
||
}
|
||
if (!isJs && isStart) {
|
||
// 处理 isJs = false 且 isStart = true 的情况
|
||
const logMessage = `正在伪造地图追踪开始的日志记录\n\n` +
|
||
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
|
||
`------------------------------\n\n` +
|
||
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
|
||
`→ 开始执行地图追踪任务: "${name}"`;
|
||
log.debug(logMessage);
|
||
}
|
||
if (!isJs && !isStart) {
|
||
// 处理 isJs = false 且 isStart = false 的情况
|
||
const logMessage = `正在伪造地图追踪结束的日志记录\n\n` +
|
||
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
|
||
`→ 脚本执行结束: "${name}", 耗时: ${durationMinutes}分${durationSeconds}秒\n\n` +
|
||
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
|
||
`------------------------------`;
|
||
log.debug(logMessage);
|
||
}
|
||
}
|
||
|
||
|
||
// 定义自定义函数 basename,用于获取文件名
|
||
function basename(filePath) {
|
||
const lastSlashIndex = filePath.lastIndexOf('\\'); // 或者使用 '/',取决于你的路径分隔符
|
||
return filePath.substring(lastSlashIndex + 1);
|
||
}
|
||
|
||
(async function () {
|
||
// 初始化所有用到的参数
|
||
const operationMode = settings.operationMode || "执行路径文件"; // 默认值为“执行路径文件”
|
||
const requiredMonsterCount = parseInt(settings.requiredMonsterCount || 1750, 10); // 默认值为1750
|
||
const excludeWaterFree = !!settings.excludeWaterFree; // 默认值为false(默认不排除)
|
||
const excludeHighRisk = !!settings.excludeHighRisk; // 默认值为false(默认不排除)
|
||
const weight = parseFloat(settings.weight || 2); // 默认值为2
|
||
const disableAutoPick = !!settings.disableAutoPick; // 默认值为false(默认不禁用)
|
||
const disableCooldownCheck = !!settings.disableCooldownCheck; // 默认值为false(默认不禁用CD检测)
|
||
const accountName = settings.accountName || "一个账户名"; // 新增账户名,默认值为“默认账户”
|
||
const pathingDir = 'pathing/'; // 初始化pathingDir参数
|
||
const enableCooldownCheck = !disableCooldownCheck; // 如果 disableCooldownCheck 未设置或为 false,则 enableCooldownCheck 为 true
|
||
|
||
// 初始化全局排除关键词配置
|
||
let globalExclusionKeywords = settings.globalExclusionKeywords || "不跑"; // 默认为不跑
|
||
|
||
const exclusionKeywordsArray = globalExclusionKeywords.split(";").map(keyword => keyword.trim()); // 使用中文分号分隔,并去除空格
|
||
|
||
// 初始化用于计数的变量
|
||
let outputFolderName = accountName; // 初始化为空
|
||
let totalMonsterCount = 0; // 筛选出的怪物总数
|
||
let selectedRoutes = []; // 筛选出的路线数组
|
||
let successCount = 0; // 成功复制的文件数量
|
||
let failCount = 0; // 失败复制的文件数量
|
||
let routeIndex = 0; // 当前筛选的路线序号
|
||
|
||
// 日志输出配置参数
|
||
log.info(
|
||
`自动小怪锄地规划 - 配置信息:` +
|
||
`操作模式=${operationMode}(默认:生成路径文件),` +
|
||
`怪物数量=${requiredMonsterCount}(默认1750),` +
|
||
`${excludeWaterFree ? '排除水免(默认:不排除)' : '包含水免(默认)'},` +
|
||
`${excludeHighRisk ? '排除高危(默认:不排除)' : '包含高危(默认)'},` +
|
||
`权重=${weight}(默认2),` +
|
||
`自动拾取=${disableAutoPick ? '禁用' : '启用'},` +
|
||
`CD检测=${disableCooldownCheck ? '禁用' : '启用'},` +
|
||
`账户名=${accountName}`
|
||
);
|
||
|
||
// 在日志中输出全局排除关键词配置信息
|
||
log.info(`全局排除关键词=${globalExclusionKeywords ? exclusionKeywordsArray.join(";") : "无"}`);
|
||
|
||
// 验证配置参数
|
||
if (
|
||
isNaN(requiredMonsterCount) ||
|
||
requiredMonsterCount < 0 ||
|
||
!Number.isInteger(requiredMonsterCount)
|
||
) {
|
||
log.warn(`怪物数量 ${requiredMonsterCount} 不符合要求,必须为0以上的整数`);
|
||
return;
|
||
}
|
||
if (isNaN(weight) || weight < 0) {
|
||
log.warn(`权重 ${weight} 不符合要求,必须为0及以上的数`);
|
||
return;
|
||
}
|
||
|
||
// 检查是否所有配置都是默认状态
|
||
const isDefaultConfig = (
|
||
operationMode === "执行路径文件" &&
|
||
requiredMonsterCount === 1750 &&
|
||
!excludeWaterFree &&
|
||
!excludeHighRisk &&
|
||
weight === 2 &&
|
||
!disableAutoPick &&
|
||
enableCooldownCheck &&
|
||
accountName === "一个账户名" &&
|
||
globalExclusionKeywords === "不跑"
|
||
);
|
||
|
||
if (isDefaultConfig) {
|
||
log.warn(`你没有修改自定义配置,请在配置组界面中右键本js以修改自定义配置`);
|
||
await sleep(5000);
|
||
}
|
||
|
||
// 根据自定义配置,如果没有禁用自动拾取,则启用自动拾取
|
||
if (!disableAutoPick) {
|
||
log.info("启用自动拾取的实时任务");
|
||
dispatcher.addTimer(new RealtimeTimer("AutoPick"));
|
||
}
|
||
|
||
|
||
const routeDir = 'route/'; // 文件夹路径
|
||
|
||
// 读取routeDir文件夹中的所有文件路径
|
||
const filesInRouteDir = file.ReadPathSync(routeDir);
|
||
|
||
// 检查路径组文件是否存在
|
||
let doGenerate = true;
|
||
for (const filePath of filesInRouteDir) {
|
||
const fileName = basename(filePath); // 提取文件名
|
||
if (fileName === `${outputFolderName}.txt`) {
|
||
doGenerate = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (doGenerate) {
|
||
log.warn('路径组文件不存在,尝试重新生成');
|
||
await sleep(5000);
|
||
}
|
||
|
||
if (operationMode !== "执行路径文件" || doGenerate) {
|
||
// 筛选、排序并选取路线
|
||
const indexPath = `index.txt`;
|
||
try {
|
||
const indexContent = await file.readText(indexPath);
|
||
const pathingFiles = indexContent.trim().split('\n').map(line => {
|
||
const parts = line.trim().split('\t');
|
||
return {
|
||
name: parts[0],
|
||
monsterCount: parseFloat(parts[1]),
|
||
secPerMonster: parseFloat(parts[2]),
|
||
monsterPerSec: parseFloat(parts[3]),
|
||
isWaterFree: parseInt(parts[4], 10) === 1,
|
||
isHighRisk: parseInt(parts[5], 10) === 1,
|
||
efficiency: weight >= 5 ? parseFloat(parts[2]) : Math.pow(parseFloat(parts[2]), weight) * parseFloat(parts[3]),
|
||
selected: 0
|
||
};
|
||
}).filter(route => !isNaN(route.monsterCount) && route.monsterCount > 0 && Number.isInteger(route.monsterCount));
|
||
|
||
const sortedPathingFiles = [...pathingFiles];
|
||
sortedPathingFiles.sort((a, b) => b.efficiency - a.efficiency || a.monsterCount - b.monsterCount);
|
||
|
||
for (const route of sortedPathingFiles) {
|
||
routeIndex++; // 每次循环时递增
|
||
const meetsKeywordCondition = exclusionKeywordsArray.every(keyword => !route.name.toLowerCase().includes(keyword));
|
||
const meetsWaterFreeCondition = !excludeWaterFree || !route.isWaterFree;
|
||
const meetsHighRiskCondition = !excludeHighRisk || !route.isHighRisk;
|
||
|
||
// 修改后的日志输出,增加当前路线的序号
|
||
log.debug(`筛选路线 ${routeIndex}(${route.name}):关键词条件 ${meetsKeywordCondition},水免条件 ${meetsWaterFreeCondition},高危条件 ${meetsHighRiskCondition}`);
|
||
|
||
if (meetsKeywordCondition && meetsWaterFreeCondition && meetsHighRiskCondition) {
|
||
totalMonsterCount += route.monsterCount;
|
||
route.selected = 1;
|
||
selectedRoutes.push(route.name);
|
||
if (totalMonsterCount >= requiredMonsterCount) break;
|
||
}
|
||
}
|
||
|
||
|
||
if (totalMonsterCount < requiredMonsterCount) {
|
||
log.warn(`数量不足,最多可以包含 ${totalMonsterCount} 只怪物,不满足所需的 ${requiredMonsterCount} 只怪物`);
|
||
}
|
||
|
||
// 根据操作模式执行相应的操作
|
||
if (operationMode === "强制刷新路径文件" || doGenerate) {
|
||
// 使用 outputFolderName 作为路径组文件的名称,并添加 .txt 扩展名
|
||
const pathGroupName = `${outputFolderName}.txt`;
|
||
const pathGroupFilePath = `route/${pathGroupName}`;
|
||
|
||
|
||
// 初始化CD信息的时间戳
|
||
const initialCDTimestamp = "::2000-01-01T00:00:00.000Z";
|
||
|
||
// 按照 index.txt 中的顺序依次检验每个路线是否需要写入文件
|
||
const resultContent = pathingFiles
|
||
.filter(route => selectedRoutes.includes(route.name))
|
||
.map(route => `${route.name}${initialCDTimestamp}`)
|
||
.join('\n');
|
||
|
||
await file.writeText(pathGroupFilePath, resultContent);
|
||
log.info(`生成成功,共计 ${selectedRoutes.length} 条路线,路径组文件已保存到 ${pathGroupFilePath}`);
|
||
} else if (operationMode === "输出地图追踪文件") {
|
||
const pathingOutDir = `pathingout/${outputFolderName}/`; // 输出文件夹路径
|
||
|
||
// 将选中的地图追踪文件复制到对应的输出文件夹
|
||
for (const routeName of selectedRoutes) {
|
||
const sourceFilePath = `${pathingDir}${routeName}.json`;
|
||
const targetFilePath = `${pathingOutDir}${routeName}.json`;
|
||
try {
|
||
const fileContent = await file.readText(sourceFilePath);
|
||
await file.writeText(targetFilePath, fileContent);
|
||
// log.debug(`文件 ${routeName}.json 已复制到 ${pathingOutDir}`);
|
||
successCount++;
|
||
} catch (error) {
|
||
log.warn(`复制文件 ${routeName}.json 时出错: ${error}`);
|
||
failCount++;
|
||
}
|
||
}
|
||
|
||
log.info(`筛选完成,共计 ${selectedRoutes.length} 条路线。`);
|
||
log.info(`文件复制完成:成功 ${successCount} 个,失败 ${failCount} 个。`);
|
||
}
|
||
} catch (error) {
|
||
log.error(`读取路径文件索引文件 ${indexPath} 时出错: ${error}`);
|
||
}
|
||
}
|
||
|
||
// 判断操作模式是否为“执行路径文件”,若是则执行路径文件
|
||
if (operationMode === "执行路径文件") {
|
||
try {
|
||
//伪造js已经结束的记录
|
||
await fakeLog("自动小怪锄地规划", true, false, 1);
|
||
// 定义路径组文件的路径,使用 outputFolderName
|
||
const pathGroupFilePath = `route/${outputFolderName}.txt`;
|
||
|
||
const pathGroupContent = await file.readText(pathGroupFilePath);
|
||
const savedRoutes = pathGroupContent.trim().split('\n');
|
||
for (let i = 0; i < savedRoutes.length; i++) {
|
||
const routeWithTimestamp = savedRoutes[i].trim();
|
||
const [routeName, routeTimestamp] = routeWithTimestamp.split('::');
|
||
log.info(`当前进度:${routeName}为第 ${i + 1}/${savedRoutes.length} 个`);
|
||
const now = new Date(); // 获取开始时间
|
||
const startTime = now.toISOString();
|
||
|
||
if (enableCooldownCheck && startTime < routeTimestamp) {
|
||
log.info(`当前路线 ${routeName} 未刷新,跳过任务`);
|
||
await sleep(10);
|
||
continue; // 跳过当前循环
|
||
}
|
||
log.info(`当前路线 ${routeName} 已刷新或未启用CD检测,执行任务`);
|
||
|
||
// 拼接路径文件的完整路径
|
||
const pathingFilePath = `pathing/${routeName}.json`;
|
||
|
||
//伪造地图追踪开始的日志记录
|
||
await fakeLog(routeName + ".json", false, true, 0); // 开始时 duration 通常为 0
|
||
|
||
// 执行路径文件
|
||
await pathingScript.runFile(pathingFilePath);
|
||
|
||
const endTime = new Date(); // 获取结束时间
|
||
const timeDiff = endTime - now; // 计算时间差(单位:毫秒)
|
||
|
||
//伪造地图追踪结束的日志记录
|
||
await fakeLog(routeName + ".json", false, false, timeDiff);
|
||
|
||
//尝试捕获任务取消的错误,捕获后终止循环
|
||
try {
|
||
await sleep(10);
|
||
} catch (error) {
|
||
log.error(`发生错误: ${error}`);
|
||
return; // 终止循环
|
||
}
|
||
|
||
// 如果启用了CD检测
|
||
if (enableCooldownCheck) {
|
||
if (timeDiff > 3000) { // 如果时间差大于3秒(3000毫秒)
|
||
// 计算新的时间戳,增加12小时
|
||
const newTimestamp = new Date(startTime).getTime() + 12 * 60 * 60 * 1000;
|
||
const formattedNewTimestamp = new Date(newTimestamp).toISOString();
|
||
const nextAvailableTime = new Date(formattedNewTimestamp).toLocaleString(); // 转换为本地时间格式
|
||
log.info(`任务 ${routeName} 运行超过10秒且当前启用刷新CD检测,下一次可用时间为 ${nextAvailableTime}`);
|
||
log.info(`新的时间戳为:${formattedNewTimestamp}`);
|
||
|
||
// 更新 savedRoutes 中对应部分的时间戳
|
||
savedRoutes[i] = `${routeName}::${formattedNewTimestamp}`;
|
||
|
||
// 立即将更新后的内容写回文件
|
||
const updatedContent = savedRoutes.join('\n');
|
||
await file.writeText(pathGroupFilePath, updatedContent);
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
log.error(`读取或写入路径组文件时出错: ${error}`);
|
||
}
|
||
|
||
//伪造一个js开始的记录(实际上没必要)
|
||
await fakeLog("自动小怪锄地规划", true, true, 0);
|
||
}
|
||
})();
|