Source: Thing.Extend/App.js


require('../Thing.Extend/BundleLoader/CustomBundleLoader');
const ThingBlueprint = require('../Thing.UE/ThingBlueprint');
const Package = require("../Thing.UE/Package");
const path = require("path");
const fs = require("fs");
const { UQualityLevel } = require("../Thing.UE/UDef");


/**
 * @description ThingUE拓展App接口
 * @class App
 */
class App extends THING.App {

    #bundle = "";
    #uApp = null;
    #complete = null;
    #progress = null;
    #exit = null;
    #qualityLevel = UQualityLevel.Epic;

    constructor(param) {
        super(param);

        this._transitionTypes = ["Default", "Wireframe", "Fire", "Mosaic"];
        this.#complete = param.complete;
        this.#progress = param.progress;
        this.#exit = param.exit;
        // this._initThingBlueprint();
        this.#initApp();

        //兼容旧版接口
        global.app = this;
        global.ueApp = this;

        // spr1ngd : redefine object propery conflicts with Proxy inside constructor
        Object.defineProperty(this, 'maxFPS', {
            enumerable: false,
            configurable: true,
            set(value) {
                this.renderFrameRate = value;
            },
            get() {
                return this.renderFrameRate;
            }
        });
    }

    get appIni() {
        return this.#uApp.appIni;
    }

    get pakIndex() {
        if (this.#uApp.appIni.pakIndexString) {
            return JSON.parse(this.#uApp.appIni.pakIndexString);
        }
        return null;
    }

    get currentBundle() {
        return this.#uApp.LoadedPakName;
    }

    set renderFrameRate(frameRate) {
        // spr1ngd: if bDefaultFPS is true , application would resume to this fps after calling rendering = true, otherwise fps would be -1
        const bDefaultFPS = true;
        this.#uApp.setMaxFPS(frameRate, bDefaultFPS);
    }

    get renderFrameRate() {
        return this.#uApp.getMaxFPS();
    }

    /**
     * @description 控制UE是否渲染\n 当UE处于页面后台时,可设置为false让UE不执行渲染,以降低GPU消耗,或者通过app.maxFPS控制渲染帧率来实现GPU消耗控制
     * @param {Boolean} value - 默认为true
     */
    set rendering(value) {
        if (this.#uApp) {
            value ? this.#uApp.ResumeRendering() : this.#uApp.PauseRendering();
        }
    }

    /**
     * get unreal engine current render quality level.\n The default level is 'Epic'
     * @return
     */
    get qualityLevel() {
        return this.#qualityLevel;
    }

    /**
     * set global render quality level.\n Do not supports to set quality level setting in sepcific scene, such as setting different quality level in exterior and interior
     * @param {UQualityLevel} value - new quality level
     */
    set qualityLevel(value) {
        this.#qualityLevel = value;
        this.#uApp.SetScalabilityLevel(value);
    }

    get bundleName() {
        return this.appIni.BundleName;
    }

    get bundlePath() {
        return this.#bundle;
    }

    #initApp() {
        if (!this.#uApp) {
            this.#uApp = UE4.World.GetActorByTag(UE4.App, '__App__');
        }

        this.#uApp.OnApplicationQuit = () => {
            if (this.#exit) {
                this.#exit();
            }
        };

        this.#uApp.OnLoading = (progress) => {
            this.updateProgress(Math.min(Math.max(progress, 0), 1));
        }

        this.#uApp.OnNodeHotReload = () => {
            this.#complete && this.#complete(this);
        }

        this.#uApp.LoadCompleted_Internal = () => {
            this.#complete && this.#complete(this);
            this.rendering = true;
        };
    }

    find(condition) {
        let object = this.root.find(condition);
        if (!object) {
            return condition.startsWith('.') ? condition.slice(1) : condition;
        }

        return super.find(condition);
    }

    query(condition, options) {
        let object = this.root.query(condition, options);
        if (!object) {
            return condition.startsWith('.') ? condition.slice(1) : condition;
        }

        return super.query(condition, options);
    }

    updateProgress(value = 0) {
        this.#progress && this.#progress(value)
    }

    loadPakBundle(bundle, complete = null) {

        this.renderFrameRate = 60;
        this.updateProgress();

        this.#bundle = this.#uApp.appIni.bPakPath || "";
        if (bundle) {
            this.#bundle = bundle;
        }

        this.#uApp.loadPak(this.#bundle, () => {
            complete && complete();
        });

        return true;
    }

    mount(pakFullPath, mountPoint, projectName) {
        this.#uApp.mount(pakFullPath, mountPoint, projectName);
    }

    unmount(projectName) {
        this.#uApp.Unmount(projectName);
    }

    /**
     * @description Force to restart nodejs inside ThingUE. (Not recommanded)
     * @description 强制重启ThingUE中nodejs模块(不推荐调用)
     */
    hotReload() {
        this.#uApp.HotReload();
    }

    preLoad() {
        this.#uApp.PreLoad();
    }

    load(groupName) {
        this.#uApp.Load(groupName);
    }

    progressPanelShow(show) {
        this.#uApp.ProgressPanelShow(show);
    }

    progressPanelChange(fileName) {
        this.#uApp.ProgressPanelChange(fileName);
    }

    printOnScreen(message) {
        this.#uApp.AddScreenMessage(message);
    }


    _initThingBlueprint() {
        new ThingBlueprint({ app: app });
    }

    loadPatches() {
        const patchPath = path.resolve(__dirname, '../../../Patches');
        let res = new Array();
        if (!fs.existsSync(patchPath))
            return res;
        const dirList = fs.readdirSync(patchPath);
        for (let index = 0; index < dirList.length; index++) {
            const folder = dirList[index];
            const pakPath = path.join('Patches', folder);
            const pak = this.loadPackage(pakPath);
            res.push(pak);
        }
        return res;
    }

    loadPackage(param) {
        const uePackage = new Package(this);
        //pa路径要放在ThingUE目录下
        const loadPath = path.resolve(__dirname, `../../../${param}`);
        uePackage.load(loadPath, true);
        return uePackage;
    }
}

THING.App = App;
module.exports = App;