unity-find-fault
Version:
A tool to find fault in unity project.
169 lines • 7.89 kB
JavaScript
import fs from 'fs-extra';
import fg from 'fast-glob';
import { toolchain } from "../toolchain.js";
import { UnityHelper } from './UnityHelper.js';
import { Record } from './Record.js';
import path from 'path';
import { Project } from 'ts-morph';
import { UIRelations } from './UIRelations.js';
export class Handyman {
async executeDelete(r) {
if (!toolchain.opts.input) {
console.error('no input file!');
return false;
}
const content = await fs.readFileSync(toolchain.opts.input, 'utf-8');
const toBeDeleteds = content.split(/\r?\n/).map(d => d.split(' ')[0]);
for (const d of toBeDeleteds) {
if (!fs.existsSync(d))
continue;
await r.collect(d);
const guid = await UnityHelper.readGUID(d + '.meta');
await fs.unlink(d);
await fs.unlink(d + '.meta');
await Record.Instance.recordGUID(guid, d);
}
return toBeDeleteds.find(d => d.endsWith('.prefab')) != null;
}
async replaceFile() {
const guidA = await UnityHelper.readGUID(path.join(toolchain.opts.projectRoot, toolchain.opts.input + '.meta'));
const guidB = await UnityHelper.readGUID(path.join(toolchain.opts.projectRoot, toolchain.opts.target + '.meta'));
const targets = await fg(UnityHelper.CompositeFileTypes.map((v) => `**/*${v}`), { cwd: toolchain.opts.projectRoot });
for (const tgt of targets) {
const tgtf = path.join(toolchain.opts.projectRoot, tgt);
const content = await fs.readFile(tgtf, 'utf-8');
const newContent = content.replaceAll(guidA, guidB);
if (newContent != content) {
await fs.writeFile(tgtf, newContent, 'utf-8');
}
}
}
async anyway() {
await this.findPotentialBugs();
}
async fixWindowAnim() {
const windowAnimController = path.join(toolchain.opts.projectRoot, 'Assets/AssetSources/ui/animation/windowOpen/windowAnim.controller');
const waguid = await UnityHelper.readGUID(windowAnimController + '.meta');
const root = path.join(toolchain.opts.projectRoot, 'Assets/AssetSources/ui');
const tsRoot = path.join(toolchain.opts.projectRoot, 'TsScripts');
const prefabs = await fg('**/*.prefab', { cwd: root });
for (const p of prefabs) {
const file = path.join(root, p);
const content = await fs.readFile(file, 'utf-8');
if (content.includes(waguid) && !content.includes('CanvasGroup:')) {
const pn = path.basename(p, '.prefab');
const tsfs = await fg(pn + '.ts', { cwd: tsRoot, caseSensitiveMatch: false });
let useAlpha = undefined;
if (tsfs.length > 0) {
const tsf = path.join(tsRoot, tsfs[0]);
const tsContent = await fs.readFile(tsf, 'utf-8');
const wpan = tsContent.matchAll(/this\._windowOpenAnimName\s=\s("|')(\w+)\1/g);
const wcan = tsContent.matchAll(/this\._windowCloseAnimName\s=\s("|')(\w+)\1/g);
for (const n of wpan) {
if (n[2] == 'windowOpen') {
useAlpha = true;
}
else {
useAlpha = false;
}
}
for (const n of wcan) {
if (n[2] == 'windowClose') {
useAlpha = true;
}
else {
useAlpha = false;
}
}
}
if (useAlpha != false) {
console.error(pn);
}
}
}
}
async fixMonster() {
const matRoot = path.join(toolchain.opts.projectRoot, 'Assets/AssetSources/modelSource/monster');
const files = await fg('**/*.mat', { cwd: matRoot });
for (const f of files) {
const matf = path.join(matRoot, f);
const content = await fs.readFile(matf, 'utf-8');
if (content.includes('7746c211250e33a419416482fc37989c')) {
let newContent = content.replace('_EdgeWidth: 0.1', '_EdgeWidth: 0.05');
// .replace('_EdgeColor: {r: 1, g: 1, b: 1, a: 1}', '_EdgeColor: {r: 0.23137252, g: 0.34346527, b: 0.9607843, a: 1}');
// if (!newContent.includes('_EdgeColor')) {
// const lines = newContent.split(/\r?\n/);
// const idx = lines.findIndex(v => v.includes('_CurvedWorldBendSettings'));
// if (idx >= 0) {
// lines.splice(idx + 1, 0, ' - _EdgeColor: {r: 0.23137252, g: 0.34346527, b: 0.9607843, a: 1}');
// }
// newContent = lines.join('\n');
// }
await fs.writeFile(matf, newContent, 'utf-8');
}
}
}
async findMissCfg() {
const a = 'D:/works/dp_minigame/trunk/Assets/AssetSources/data';
const aCfgs = await fg('*.json', { cwd: a });
const b = 'D:/works/dpxyx/trunk/project/Assets/AssetSources/data';
const bCfgs = await fg('*.json', { cwd: b });
for (const c of aCfgs) {
if (!bCfgs.includes(c)) {
console.error(c);
}
}
}
async findPotentialBugs() {
await UIRelations.build();
const allForms = UIRelations.getAllForms();
const tsRoot = path.join(toolchain.opts.projectRoot, 'TsScripts');
const project = new Project({
tsConfigFilePath: path.join(tsRoot, 'tsconfig.json')
});
for (const f of allForms) {
const src = project.getSourceFile(f.tsClass.sourceFile);
if (!src) {
console.error('SourceFile not exists:', f.tsClass.sourceFile);
continue;
}
const classes = src.getClasses();
for (const cls of classes) {
if (cls.getName() != f.tsClass.className)
continue;
if (cls.getConstructors()[0]?.getText().includes('this._cacheForm = true'))
continue;
const mthds = cls.getMethods();
for (const mthd of mthds) {
const body = mthd.getBody();
if (body == null)
continue;
const startLine = body.getStartLineNumber();
const lines = body.getText().split(/\r?\n/);
let closeLine = -1, lineCnt = lines.length;
for (let i = 0; i < lineCnt; i++) {
const line = lines[i];
if (!line.match(/^\s*\/\//) && (line.includes('.doClose()') || line.includes('.close()'))) {
closeLine = i;
break;
}
}
if (closeLine >= 0 && closeLine < lineCnt - 1) {
const nextLine = lines[closeLine + 1];
if (nextLine.includes('return'))
continue;
for (let i = closeLine + 1; i < lineCnt; i++) {
const line = lines[i];
if (line.includes('this.')) {
console.error(`try to access properties after dispose: ${f.tsClass.sourceFile}:${startLine + i}`);
console.error(line);
break;
}
}
}
}
}
}
}
}
//# sourceMappingURL=Handyman.js.map