Example #1
0
    /// <summary>
    /// Create Emscripten Data package with Necessary Packages.
    ///
    /// </summary>
    public void CreateEmscriptenDataPackage(string PackagePath, string FinalDataLocation)
    {
        // we need to operate in the root
        using (new PushedDirectory(Path.Combine(PackagePath)))
        {
            string PythonPath   = HTML5SDKInfo.PythonPath();
            string PackagerPath = HTML5SDKInfo.EmscriptenPackager();

            string CmdLine = string.Format("\"{0}\" \"{1}\" --preload {2}\\ --js-output=\"{1}.js\"", PackagerPath, FinalDataLocation, SC.ShortProjectName);
            CommandUtils.RunAndLog(CommandUtils.CmdEnv, PythonPath, CmdLine);
        }
    }
Example #2
0
    /// <summary>
    /// Create Emscripten Data package with Necessary Packages.
    ///
    /// </summary>
    public void CreateEmscriptenDataPackage(string PackagePath, string FinalDataLocation)
    {
        string PythonPath   = HTML5SDKInfo.Python().FullName;
        string PackagerPath = HTML5SDKInfo.EmscriptenPackager();

        using (new ScopedEnvVar("EM_CONFIG", HTML5SDKInfo.DOT_EMSCRIPTEN))
        {
            // we need to operate in the root
            using (new PushedDirectory(Path.Combine(PackagePath)))
            {
                string CmdLine = string.Format("\"{0}\" \"{1}\" --preload \"{2}\" --js-output=\"{1}.js\" --no-heap-copy", PackagerPath, FinalDataLocation, SC.ShortProjectName);
                CommandUtils.RunAndLog(CommandUtils.CmdEnv, PythonPath, CmdLine);
            }
        }
    }
    public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
    {
        Log("Package {0}", Params.RawProjectPath);

        Log("Setting Emscripten SDK for packaging..");
        HTML5SDKInfo.SetupEmscriptenTemp();
        HTML5SDKInfo.SetUpEmscriptenConfigFile();

        string PackagePath = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Binaries", "HTML5");

        if (!Directory.Exists(PackagePath))
        {
            Directory.CreateDirectory(PackagePath);
        }

        // ini configurations
        var  ConfigCache    = UnrealBuildTool.ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(Params.RawProjectPath), UnrealTargetPlatform.HTML5);
        bool targetingAsmjs = false;       // inverted checked - this will be going away soon...
        bool targetWebGL1   = false;       // inverted checked - this will be going away soon...

        if (ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "TargetAsmjs", out targetingAsmjs))
        {
            targetingWasm = !targetingAsmjs;
        }
        if (ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "TargetWebGL1", out targetWebGL1))
        {
            targetWebGL2 = !targetWebGL1;
        }

        // Debug and Development builds are not uncompressed to:
        // - speed up iteration times
        // - ensure (IndexedDB) data are not cached/used
        // Shipping builds "can be":
        // - compressed
        // - (IndexedDB) cached
        if (Params.ClientConfigsToBuild[0].ToString() == "Shipping")
        {
            ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "Compressed", out Compressed);
            ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "EnableIndexedDB", out enableIndexedDB);
        }
        Log("HTML5Platform.Automation: TargetWasm = " + targetingWasm);
        Log("HTML5Platform.Automation: TargetWebGL2 = " + targetWebGL2);
        Log("HTML5Platform.Automation: Compressed = " + Compressed);
        Log("HTML5Platform.Automation: EnableIndexedDB = " + enableIndexedDB);

        string FinalDataLocation = Path.Combine(PackagePath, Params.ShortProjectName) + ".data";

        if (HTMLPakAutomation.CanCreateMapPaks(Params))
        {
            HTMLPakAutomation PakAutomation = new HTMLPakAutomation(Params, SC);

            // Create Necessary Paks.
            PakAutomation.CreateEnginePak();
            PakAutomation.CreateGamePak();
            PakAutomation.CreateContentDirectoryPak();

            // Create Emscripten Package from Necessary Paks. - This will be the VFS.
            PakAutomation.CreateEmscriptenDataPackage(PackagePath, FinalDataLocation);

            // Create All Map Paks which  will be downloaded on the fly.
            PakAutomation.CreateMapPak();

            // Create Delta Paks if setup.
            List <string> Paks = new List <string>();
            ConfigCache.GetArray("/Script/HTML5PlatformEditor.HTML5TargetSettings", "LevelTransitions", out Paks);

            if (Paks != null)
            {
                foreach (var Pak in Paks)
                {
                    var    Matched = Regex.Matches(Pak, "\"[^\"]+\"", RegexOptions.IgnoreCase);
                    string MapFrom = Path.GetFileNameWithoutExtension(Matched[0].ToString().Replace("\"", ""));
                    string MapTo   = Path.GetFileNameWithoutExtension(Matched[1].ToString().Replace("\"", ""));
                    PakAutomation.CreateDeltaMapPaks(MapFrom, MapTo);
                }
            }
        }
        else
        {
            // we need to operate in the root
            string PythonPath   = HTML5SDKInfo.Python();
            string PackagerPath = HTML5SDKInfo.EmscriptenPackager();

            using (new ScopedEnvVar("EM_CONFIG", HTML5SDKInfo.DOT_EMSCRIPTEN))
            {
                using (new PushedDirectory(Path.Combine(Params.BaseStageDirectory, "HTML5")))
                {
                    string CmdLine = string.Format("\"{0}\" \"{1}\" --preload . --js-output=\"{1}.js\" --no-heap-copy", PackagerPath, FinalDataLocation);
                    RunAndLog(CmdEnv, PythonPath, CmdLine);
                }
            }
        }

        // copy the "Executable" to the package directory
        string GameBasename = Path.GetFileNameWithoutExtension(Params.ProjectGameExeFilename);

        if (Params.ClientConfigsToBuild[0].ToString() != "Development")
        {
            GameBasename += "-HTML5-" + Params.ClientConfigsToBuild[0].ToString();
        }
        // no extension
        string GameBasepath            = Path.GetDirectoryName(Params.ProjectGameExeFilename);
        string FullGameBasePath        = Path.Combine(GameBasepath, GameBasename);
        string FullPackageGameBasePath = Path.Combine(PackagePath, GameBasename);

        // with extension
        string GameExe                = GameBasename + ".js";
        string FullGameExePath        = Path.Combine(GameBasepath, GameExe);
        string FullPackageGameExePath = Path.Combine(PackagePath, GameExe);
        // special case -- this will be removed when asm.js has been deprecated
        string ASMJS_FullPackageGameExePath = Path.Combine(PackagePath, GameBasename + "_asm.js");


        // ensure the ue4game binary exists, if applicable
        if (!SC.IsCodeBasedProject && !FileExists_NoExceptions(FullGameExePath))
        {
            Log("Failed to find game application " + FullGameExePath);
            throw new AutomationException(ExitCode.Error_MissingExecutable, "Stage Failed. Could not find application {0}. You may need to build the UE4 project with your target configuration and platform.", FullGameExePath);
        }

        if (FullGameExePath != FullPackageGameExePath)         // TODO: remove this check
        {
            File.Copy(FullGameExePath + ".symbols", FullPackageGameExePath + ".symbols", true);
            if (targetingWasm)
            {
                File.Copy(FullGameBasePath + ".wasm", FullPackageGameBasePath + ".wasm", true);
                File.Copy(FullGameExePath, FullPackageGameExePath, true);
            }
            else
            {
                File.Copy(FullGameExePath + ".mem", FullPackageGameExePath + ".mem", true);
                File.Copy(FullGameBasePath + ".asm.js", FullPackageGameBasePath + ".asm.js", true);
            }
        }

        File.SetAttributes(FullPackageGameExePath + ".symbols", FileAttributes.Normal);
        if (targetingWasm)
        {
            File.SetAttributes(FullPackageGameBasePath + ".wasm", FileAttributes.Normal);
            File.SetAttributes(FullPackageGameExePath, FileAttributes.Normal);
        }
        else
        {
            File.SetAttributes(FullPackageGameExePath + ".mem", FileAttributes.Normal);
            File.SetAttributes(FullPackageGameBasePath + ".asm.js", FileAttributes.Normal);
            File.Copy(FullGameExePath, ASMJS_FullPackageGameExePath, true);             // --separate-asm // UE-45058
            File.SetAttributes(ASMJS_FullPackageGameExePath, FileAttributes.Normal);
        }


        // put the HTML file to the package directory
        string TemplateFile = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/HTML5/GameX.html.template");
        string OutputFile   = Path.Combine(PackagePath,
                                           (Params.ClientConfigsToBuild[0].ToString() != "Development" ?
                                            (Params.ShortProjectName + "-HTML5-" + Params.ClientConfigsToBuild[0].ToString()) :
                                            Params.ShortProjectName)) + ".html";

        GenerateFileFromTemplate(TemplateFile,
                                 OutputFile,
                                 Params.ShortProjectName,
                                 Params.ClientConfigsToBuild[0].ToString(),
                                 Params.StageCommandline,
                                 !Params.IsCodeBasedProject,
                                 HTML5SDKInfo.HeapSize(ConfigCache, Params.ClientConfigsToBuild[0].ToString()));

        string MacBashTemplateFile = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/HTML5/RunMacHTML5LaunchHelper.command.template");
        string MacBashOutputFile   = Path.Combine(PackagePath, "RunMacHTML5LaunchHelper.command");
        string MonoPath            = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/BatchFiles/Mac/SetupMono.sh");

        GenerateMacCommandFromTemplate(MacBashTemplateFile, MacBashOutputFile, MonoPath);

        string htaccessTemplate = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/HTML5/htaccess.template");
        string htaccesspath     = Path.Combine(PackagePath, ".htaccess");

        if (File.Exists(htaccesspath))
        {
            FileAttributes attributes = File.GetAttributes(htaccesspath);
            if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
            {
                attributes &= ~FileAttributes.ReadOnly;
                File.SetAttributes(htaccesspath, attributes);
            }
        }
        File.Copy(htaccessTemplate, htaccesspath, true);

        string JSDir  = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/HTML5");
        string OutDir = PackagePath;

        // Gather utlity .js files and combine into one file
        string[] UtilityJavaScriptFiles = Directory.GetFiles(JSDir, "*.js");

        string DestinationFile = OutDir + "/Utility.js";

        File.Delete(DestinationFile);
        foreach (var UtilityFile in UtilityJavaScriptFiles)
        {
            string Data = File.ReadAllText(UtilityFile);
            File.AppendAllText(DestinationFile, Data);
        }

        if (Compressed)
        {
            Log("Build configuration is " + Params.ClientConfigsToBuild[0].ToString() + ", so (gzip) compressing files for web servers.");

            // Compress all files. These are independent tasks which can be threaded.
            List <Task> CompressionTasks = new List <Task>();

            // Note that the main .data file is never gzip compressed, because we rely on UnrealPak having compressed it already (above), and gzipping the pak file on
            // top is showing negligible benefit. Check the console output from UnrealPak run to verify that it is indeed compressed.
//			CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FinalDataLocation, FinalDataLocation + "gz")));

            // data file .js driver.
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FinalDataLocation + ".js", FinalDataLocation + ".jsgz")));

            if (targetingWasm)
            {
                // main game code
                CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FullPackageGameBasePath + ".wasm", FullPackageGameBasePath + ".wasmgz")));
                // main js.
                CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FullPackageGameExePath, FullPackageGameExePath + "gz")));
            }
            else
            {
                // mem init file.
                CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FullPackageGameExePath + ".mem", FullPackageGameExePath + ".memgz")));
                // main js.
                CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FullPackageGameBasePath + ".asm.js", FullPackageGameBasePath + ".asm.jsgz")));
                // main game code
                CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(ASMJS_FullPackageGameExePath, ASMJS_FullPackageGameExePath + "gz")));
            }

            // symbols file.
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(FullPackageGameExePath + ".symbols", FullPackageGameExePath + ".symbolsgz")));

            // Utility
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(OutDir + "/Utility.js", OutDir + "/Utility.jsgz")));

            Task.WaitAll(CompressionTasks.ToArray());
        }
        else
        {
            Log("Build configuration is " + Params.ClientConfigsToBuild[0].ToString() + ", so not compressing. Build Shipping configuration to compress files to save space.");

            // nuke old compressed files to prevent using stale files
            File.Delete(FinalDataLocation + "gz");
            File.Delete(FinalDataLocation + ".jsgz");
            File.Delete(FullPackageGameExePath + "gz");
            File.Delete(FullPackageGameBasePath + ".wasmgz");
            File.Delete(FullPackageGameExePath + ".memgz");
            File.Delete(FullPackageGameExePath + ".symbolsgz");
            File.Delete(OutDir + "/Utility.jsgz");
        }

        File.Copy(CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/HTML5LaunchHelper.exe"), CombinePaths(OutDir, "HTML5LaunchHelper.exe"), true);
//		Task.WaitAll(CompressionTasks);
        PrintRunTime();
    }
    public override ProcessResult RunClient(ERunOptions ClientRunFlags, string ClientApp, string ClientCmdLine, ProjectParams Params)
    {
        // look for browser
        var           ConfigCache = new UnrealBuildTool.ConfigCacheIni(UnrealTargetPlatform.HTML5, "Engine", Path.GetDirectoryName(Params.RawProjectPath), CombinePaths(CmdEnv.LocalRoot, "Engine"));
        bool          ok          = false;
        List <string> Devices;
        string        browserPath = "";
        string        DeviceName  = Params.Device.Split('@')[1];

        DeviceName = DeviceName.Substring(0, DeviceName.LastIndexOf(" on "));

        if (ConfigCache.GetArray("/Script/HTML5PlatformEditor.HTML5SDKSettings", "DeviceMap", out Devices))
        {
            foreach (var Dev in Devices)
            {
                var Matched = Regex.Match(Dev, "\\(DeviceName=\"(.*)\",DevicePath=\\(FilePath=\"(.*)\"\\)\\)", RegexOptions.IgnoreCase);
                if (Matched.Success && Matched.Groups[1].ToString() == DeviceName)
                {
                    browserPath = Matched.Groups[2].ToString();
                    ok          = true;
                    break;
                }
            }
        }

        if (!ok && HTML5SDKInfo.bAllowFallbackSDKSettings)
        {
            string DeviceSection;

            if (Utils.IsRunningOnMono)
            {
                DeviceSection = "HTML5DevicesMac";
            }
            else
            {
                DeviceSection = "HTML5DevicesWindows";
            }

            ok = ConfigCache.GetString(DeviceSection, DeviceName, out browserPath);
        }

        if (!ok)
        {
            throw new System.Exception("Incorrect browser configuration in HTML5Engine.ini ");
        }

        // open the webpage
        Int32 ServerPort = 8000;

        ConfigCache.GetInt32("/Script/HTML5PlatformEditor.HTML5TargetSettings", "DeployServerPort", out ServerPort);
        string WorkingDirectory = Path.GetDirectoryName(ClientApp);
        string url  = Path.GetFileName(ClientApp) + ".html";
        string args = "-m ";
        // Are we running via cook on the fly server?
        // find our http url - This is awkward because RunClient doesn't have real information that NFS is running or not.
        bool IsCookOnTheFly = false;

        // 9/24/2014 @fixme - All this is convoluted, clean up.
        // looks like cookonthefly commandline stopped adding protocol or the port :/ hard coding to DEFAULT_TCP_FILE_SERVING_PORT+1 (DEFAULT_HTTP_FILE_SERVING_PORT)
        // This will fail if the NFS server is started with a different port - we need to modify driver .cs script to pass in IP/Port data correctly.

        if (ClientCmdLine.Contains("filehostip"))
        {
            IsCookOnTheFly = true;
            url            = "http://127.0.0.1:41898/" + url;
        }

        if (IsCookOnTheFly)
        {
            url += "?cookonthefly=true";
        }
        else
        {
            url   = String.Format("http://127.0.0.1:{0}/{1}", ServerPort, url);
            args += String.Format("-h -s {0} ", ServerPort);
        }

        // Check HTML5LaunchHelper source for command line args
        var LowerBrowserPath = browserPath.ToLower();

        args += String.Format("-b \"{0}\" -p \"{1}\" -w \"{2}\" ", browserPath, HTML5SDKInfo.PythonPath(), WorkingDirectory);
        args += url + " ";
        var ProfileDirectory = Path.Combine(Utils.GetUserSettingDirectory(), "UE4_HTML5", "user");

        if (LowerBrowserPath.Contains("chrome"))
        {
            args += String.Format("--user-data-dir=\"{0}\" --enable-logging --no-first-run", Path.Combine(ProfileDirectory, "chrome"));
        }
        else if (LowerBrowserPath.Contains("firefox"))
        {
            args += String.Format("-no-remote -profile \"{0}\" -jsconsole", Path.Combine(ProfileDirectory, "firefox"));
        }
        //else if (browserPath.Contains("Safari")) {}

        var           LaunchHelperPath = CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/HTML5LaunchHelper.exe");
        ProcessResult BrowserProcess   = Run(LaunchHelperPath, args, null, ClientRunFlags | ERunOptions.NoWaitForExit);

        return(BrowserProcess);
    }
    public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
    {
        Log("Package {0}", Params.RawProjectPath);

        string PackagePath = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath), "Binaries", "HTML5");

        if (!Directory.Exists(PackagePath))
        {
            Directory.CreateDirectory(PackagePath);
        }
        string FinalDataLocation = Path.Combine(PackagePath, Params.ShortProjectName) + ".data";

        // we need to operate in the root
        using (new PushedDirectory(Path.Combine(Params.BaseStageDirectory, "HTML5")))
        {
            string PythonPath   = HTML5SDKInfo.PythonPath();
            string PackagerPath = HTML5SDKInfo.EmscriptenPackager();

            string CmdLine = string.Format("\"{0}\" \"{1}\" --preload . --js-output=\"{1}.js\"", PackagerPath, FinalDataLocation);
            RunAndLog(CmdEnv, PythonPath, CmdLine);
        }

        // copy the "Executable" to the package directory
        string GameExe = Path.GetFileNameWithoutExtension(Params.ProjectGameExeFilename);

        if (Params.ClientConfigsToBuild[0].ToString() != "Development")
        {
            GameExe += "-HTML5-" + Params.ClientConfigsToBuild[0].ToString();
        }
        GameExe += ".js";

        // ensure the ue4game binary exists, if applicable
        string FullGameExePath = Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe);

        if (!SC.IsCodeBasedProject && !FileExists_NoExceptions(FullGameExePath))
        {
            Log("Failed to find game application " + FullGameExePath);
            AutomationTool.ErrorReporter.Error("Stage Failed.", (int)AutomationTool.ErrorCodes.Error_MissingExecutable);
            throw new AutomationException("Could not find application {0}. You may need to build the UE4 project with your target configuration and platform.", FullGameExePath);
        }

        if (Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe) != Path.Combine(PackagePath, GameExe))
        {
            File.Copy(Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe), Path.Combine(PackagePath, GameExe), true);
            File.Copy(Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe) + ".mem", Path.Combine(PackagePath, GameExe) + ".mem", true);
        }
        File.SetAttributes(Path.Combine(PackagePath, GameExe), FileAttributes.Normal);
        File.SetAttributes(Path.Combine(PackagePath, GameExe) + ".mem", FileAttributes.Normal);

        // put the HTML file to the package directory
        string TemplateFile = Path.Combine(CombinePaths(CmdEnv.LocalRoot, "Engine"), "Build", "HTML5", "Game.html.template");
        string OutputFile   = Path.Combine(PackagePath, (Params.ClientConfigsToBuild[0].ToString() != "Development" ? (Params.ShortProjectName + "-HTML5-" + Params.ClientConfigsToBuild[0].ToString()) : Params.ShortProjectName)) + ".html";

        // find Heap Size.
        ulong HeapSize;
        var   ConfigCache = new UnrealBuildTool.ConfigCacheIni(UnrealTargetPlatform.HTML5, "Engine", Path.GetDirectoryName(Params.RawProjectPath), CombinePaths(CmdEnv.LocalRoot, "Engine"));

        int ConfigHeapSize = 0;
        // Valuer set by Editor UI
        var bGotHeapSize = ConfigCache.GetInt32("/Script/HTML5PlatformEditor.HTML5TargetSettings", "HeapSize" + Params.ClientConfigsToBuild[0].ToString(), out ConfigHeapSize);

        // Fallback if the previous method failed
        if (!bGotHeapSize && !ConfigCache.GetInt32("BuildSettings", "HeapSize" + Params.ClientConfigsToBuild[0].ToString(), out ConfigHeapSize))     // in Megs.
        {
            // we couldn't find a per config heap size, look for a common one.
            if (!ConfigCache.GetInt32("BuildSettings", "HeapSize", out ConfigHeapSize))
            {
                ConfigHeapSize = Params.IsCodeBasedProject ? 1024 : 512;
                Log("Could not find Heap Size setting in .ini for Client config {0}", Params.ClientConfigsToBuild[0].ToString());
            }
        }

        HeapSize = (ulong)ConfigHeapSize * 1024L * 1024L;     // convert to bytes.
        Log("Setting Heap size to {0} Mb ", ConfigHeapSize);


        GenerateFileFromTemplate(TemplateFile, OutputFile, Params.ShortProjectName, Params.ClientConfigsToBuild[0].ToString(), Params.StageCommandline, !Params.IsCodeBasedProject, HeapSize);

        // copy the jstorage files to the binaries directory
        string JSDir  = Path.Combine(CombinePaths(CmdEnv.LocalRoot, "Engine"), "Build", "HTML5");
        string OutDir = PackagePath;

        File.Copy(JSDir + "/json2.js", OutDir + "/json2.js", true);
        File.SetAttributes(OutDir + "/json2.js", FileAttributes.Normal);
        File.Copy(JSDir + "/jStorage.js", OutDir + "/jStorage.js", true);
        File.SetAttributes(OutDir + "/jStorage.js", FileAttributes.Normal);
        File.Copy(JSDir + "/moz_binarystring.js", OutDir + "/moz_binarystring.js", true);
        File.SetAttributes(OutDir + "/moz_binarystring.js", FileAttributes.Normal);
        PrintRunTime();
    }
Example #6
0
    protected void GenerateFileFromTemplate(string InTemplateFile, string InOutputFile, ProjectParams Params, ConfigHierarchy ConfigCache)
    {
        bool   IsContentOnly        = !Params.IsCodeBasedProject;
        string ProjectConfiguration = Params.ClientConfigsToBuild[0].ToString();

        string UE4GameName = IsContentOnly ? "UE4Game" : Params.ShortProjectName;
        string ProjectName = Params.ShortProjectName;

        if (ProjectConfiguration != "Development")
        {
            UE4GameName += "-HTML5-" + ProjectConfiguration;
            ProjectName += "-HTML5-" + ProjectConfiguration;
        }

        string CanvasScaleMode;

        ConfigCache.GetString("/Script/HTML5PlatformEditor.HTML5TargetSettings", "CanvasScalingMode", out CanvasScaleMode);

        string HeapSize = HTML5SDKInfo.HeapSize(ConfigCache, ProjectConfiguration).ToString();

        StringBuilder outputContents = new StringBuilder();

        using (StreamReader reader = new StreamReader(InTemplateFile))
        {
            string LineStr = null;
            while (reader.Peek() != -1)
            {
                LineStr = reader.ReadLine();
                if (LineStr.Contains("%TIMESTAMP%"))
                {
                    string TimeStamp = DateTime.UtcNow.ToString("yyyyMMddHHmm");
                    LineStr = LineStr.Replace("%TIMESTAMP%", TimeStamp);
                }

                if (LineStr.Contains("%SHORTNAME%"))
                {
                    LineStr = LineStr.Replace("%SHORTNAME%", Params.ShortProjectName);
                }

                if (LineStr.Contains("%UE4GAMENAME%"))
                {
                    LineStr = LineStr.Replace("%UE4GAMENAME%", UE4GameName);
                }

                if (LineStr.Contains("%PROJECTNAME%"))
                {
                    LineStr = LineStr.Replace("%PROJECTNAME%", ProjectName);
                }

                if (LineStr.Contains("%SERVE_COMPRESSED%"))
                {
                    LineStr = LineStr.Replace("%SERVE_COMPRESSED%", Compressed ? "true" : "false");
                }

                if (LineStr.Contains("%DISABLE_INDEXEDDB%"))
                {
                    LineStr = LineStr.Replace("%DISABLE_INDEXEDDB%",
                                              enableIndexedDB ? "" : "enableReadFromIndexedDB = false;\nenableWriteToIndexedDB = false;");
                }

                if (LineStr.Contains("%HEAPSIZE%"))
                {
                    LineStr = LineStr.Replace("%HEAPSIZE%", HeapSize + " * 1024 * 1024");
                }

                if (LineStr.Contains("%UE4CMDLINE%"))
                {
                    string ArgumentString = "'../../../" + Params.ShortProjectName + "/" + Params.ShortProjectName + ".uproject',";
                    ArgumentString += "'-stdout',";                     // suppress double printing to console.log
                    LineStr         = LineStr.Replace("%UE4CMDLINE%", ArgumentString);
                }

                if (LineStr.Contains("%CANVASSCALEMODE%"))
                {
                    string mode = "2 /*ASPECT*/";                     // default
                    if (CanvasScaleMode.Equals("stretch", StringComparison.InvariantCultureIgnoreCase))
                    {
                        mode = "1 /*STRETCH*/";
                    }
                    else if (CanvasScaleMode.Equals("fixed", StringComparison.InvariantCultureIgnoreCase))
                    {
                        mode = "3 /*FIXED*/";
                    }
                    LineStr = LineStr.Replace("%CANVASSCALEMODE%", mode);
                }

                outputContents.AppendLine(LineStr);
            }
        }

        if (outputContents.Length > 0)
        {
            // Save the file
            try
            {
                Directory.CreateDirectory(Path.GetDirectoryName(InOutputFile));
                File.WriteAllText(InOutputFile, outputContents.ToString(), Encoding.UTF8);
            }
            catch (Exception)
            {
                // Unable to write to the project file.
            }
        }
    }
Example #7
0
    public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
    {
        LogInformation("Package {0}", Params.RawProjectPath);

        LogInformation("Setting Emscripten SDK for packaging..");
        HTML5SDKInfo.SetupEmscriptenTemp();
        HTML5SDKInfo.SetUpEmscriptenConfigFile();

        // ----------------------------------------
        // ini configurations
        ConfigHierarchy ConfigCache = UnrealBuildTool.ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(Params.RawProjectPath), UnrealTargetPlatform.HTML5);

        // Debug and Development builds are not compressed to:
        // - speed up iteration times
        // - ensure (IndexedDB) data are not cached/used
        // Shipping builds "can be":
        // - compressed
        // - (IndexedDB) cached
        string ProjectConfiguration = Params.ClientConfigsToBuild[0].ToString();

        if (ProjectConfiguration == "Shipping")
        {
            ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "Compressed", out Compressed);
            ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "EnableIndexedDB", out enableIndexedDB);
        }
        LogInformation("HTML5Platform.Automation: Compressed = " + Compressed);
        LogInformation("HTML5Platform.Automation: EnableIndexedDB = " + enableIndexedDB);

        // ----------------------------------------
        // package directory
        string PackagePath = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Binaries", "HTML5");

        if (!Directory.Exists(PackagePath))
        {
            Directory.CreateDirectory(PackagePath);
        }
        string _ProjectNameExtra  = ProjectConfiguration != "Development" ? "-HTML5-" + ProjectConfiguration : "";
        string _ProjectFullpath   = Params.GetProjectExeForPlatform(UnrealTargetPlatform.HTML5).ToString();
        string _ProjectFilename   = Path.GetFileNameWithoutExtension(_ProjectFullpath) + _ProjectNameExtra;
        string SrcUE4GameBasename = Path.Combine(Path.GetDirectoryName(_ProjectFullpath), _ProjectFilename);
        string UE4GameBasename    = Path.Combine(PackagePath, _ProjectFilename);
        string ProjectBasename    = Path.Combine(PackagePath, Params.ShortProjectName + _ProjectNameExtra);

        // ----------------------------------------
        // packaging
        if (HTMLPakAutomation.CanCreateMapPaks(Params))
        {
            HTMLPakAutomation PakAutomation = new HTMLPakAutomation(Params, SC);

            // Create Necessary Paks.
            PakAutomation.CreateEnginePak();
            PakAutomation.CreateGamePak();
            PakAutomation.CreateContentDirectoryPak();

            // Create Emscripten Package from Necessary Paks. - This will be the VFS.
            PakAutomation.CreateEmscriptenDataPackage(PackagePath, ProjectBasename + ".data");

            // Create All Map Paks which  will be downloaded on the fly.
            PakAutomation.CreateMapPak();

            // Create Delta Paks if setup.
            List <string> Paks = new List <string>();
            ConfigCache.GetArray("/Script/HTML5PlatformEditor.HTML5TargetSettings", "LevelTransitions", out Paks);

            if (Paks != null)
            {
                foreach (var Pak in Paks)
                {
                    var    Matched = Regex.Matches(Pak, "\"[^\"]+\"", RegexOptions.IgnoreCase);
                    string MapFrom = Path.GetFileNameWithoutExtension(Matched[0].ToString().Replace("\"", ""));
                    string MapTo   = Path.GetFileNameWithoutExtension(Matched[1].ToString().Replace("\"", ""));
                    PakAutomation.CreateDeltaMapPaks(MapFrom, MapTo);
                }
            }
        }
        else
        {
            // we need to operate in the root
            string PythonPath     = HTML5SDKInfo.Python();
            string EmPackagerPath = HTML5SDKInfo.EmscriptenPackager();

            using (new ScopedEnvVar("EM_CONFIG", HTML5SDKInfo.DOT_EMSCRIPTEN))
            {
                using (new PushedDirectory(Path.Combine(Params.BaseStageDirectory, "HTML5")))
                {
                    string CmdLine = string.Format("\"{0}\" \"{1}\" --preload . --js-output=\"{1}.js\" --no-heap-copy", EmPackagerPath, ProjectBasename + ".data");
                    RunAndLog(CmdEnv, PythonPath, CmdLine);
                }
            }
        }


        // ----------------------------------------
        // copy to package directory

        // ensure the ue4game binary exists, if applicable
        if (!FileExists_NoExceptions(SrcUE4GameBasename + ".js"))
        {
            LogInformation("Failed to find game application " + SrcUE4GameBasename + ".js");
            throw new AutomationException(ExitCode.Error_MissingExecutable, "Stage Failed. Could not find application {0}. You may need to build the UE4 project with your target configuration and platform.", SrcUE4GameBasename + ".js");
        }

        if (!Params.IsCodeBasedProject)
        {
            // template project - need to copy over UE4Game.*
            File.Copy(SrcUE4GameBasename + ".wasm", UE4GameBasename + ".wasm", true);
            File.Copy(SrcUE4GameBasename + ".js", UE4GameBasename + ".js", true);
            File.Copy(SrcUE4GameBasename + ".js.symbols", UE4GameBasename + ".js.symbols", true);

            File.SetAttributes(UE4GameBasename + ".wasm", FileAttributes.Normal);
            File.SetAttributes(UE4GameBasename + ".js", FileAttributes.Normal);
            File.SetAttributes(UE4GameBasename + ".js.symbols", FileAttributes.Normal);
        }
        // else, c++ projects will compile "to" PackagePath

        // note: ( ProjectBasename + ".data" ) already created above (!HTMLPakAutomation.CanCreateMapPaks())


        // ----------------------------------------
        // generate HTML files to the package directory

        // custom HTML, JS (if any), and CSS (if any) template files
        string LocalBuildPath = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/HTML5");
        string BuildPath      = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Build", "HTML5");
        string TemplateFile   = CombinePaths(BuildPath, "project_template.html");

        if (!File.Exists(TemplateFile))
        {
            // fall back to default UE4 template files
            BuildPath    = LocalBuildPath;
            TemplateFile = CombinePaths(BuildPath, "project_template.html");
        }
        GenerateFileFromTemplate(TemplateFile, ProjectBasename + ".html", Params, ConfigCache);

        TemplateFile = CombinePaths(BuildPath, "project_template.js");
        if (File.Exists(TemplateFile))
        {
            GenerateFileFromTemplate(TemplateFile, ProjectBasename + ".UE4.js", Params, ConfigCache);
        }

        TemplateFile = CombinePaths(BuildPath, "project_template.css");
        if (File.Exists(TemplateFile))
        {
            GenerateFileFromTemplate(TemplateFile, ProjectBasename + ".css", Params, ConfigCache);
        }


        // ----------------------------------------
        // (development) support files
        string MacBashTemplateFile = CombinePaths(LocalBuildPath, "RunMacHTML5LaunchHelper_template.command");
        string MacBashOutputFile   = Path.Combine(PackagePath, "RunMacHTML5LaunchHelper.command");
        string MonoPath            = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/BatchFiles/Mac/SetupMono.sh");

        GenerateMacCommandFromTemplate(MacBashTemplateFile, MacBashOutputFile, MonoPath);
        // ........................................
        string htaccesspath = Path.Combine(PackagePath, ".htaccess");

        if (File.Exists(htaccesspath))
        {
            FileAttributes attributes = File.GetAttributes(htaccesspath);
            if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
            {
                attributes &= ~FileAttributes.ReadOnly;
                File.SetAttributes(htaccesspath, attributes);
            }
        }
        File.Copy(CombinePaths(LocalBuildPath, "htaccess_template.txt"), htaccesspath, true);


        // ----------------------------------------
        // final copies

        // Gather utlity .js files and combine into one file
        string DestinationFile = PackagePath + "/Utility.js";

        File.Delete(DestinationFile);
        // spelling this out - one file at a time (i.e. don't slurp in project_template.js)
        File.AppendAllText(DestinationFile, File.ReadAllText(CombinePaths(LocalBuildPath, "json2.js")));
        File.AppendAllText(DestinationFile, File.ReadAllText(CombinePaths(LocalBuildPath, "jstorage.js")));
        File.AppendAllText(DestinationFile, File.ReadAllText(CombinePaths(LocalBuildPath, "moz_binarystring.js")));

        if (Compressed)
        {
            LogInformation("Build configuration is " + ProjectConfiguration + ", so (gzip) compressing files for web servers.");

            // Compress all files. These are independent tasks which can be threaded.
            List <Task> CompressionTasks = new List <Task>();

            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(UE4GameBasename + ".wasm", UE4GameBasename + ".wasmgz")));                            // main game code
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(UE4GameBasename + ".js", UE4GameBasename + ".jsgz")));                                // main js (emscripten)
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(UE4GameBasename + ".js.symbols", UE4GameBasename + ".js.symbolsgz")));                // symbols fil.
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(PackagePath + "/Utility.js", PackagePath + "/Utility.jsgz")));                        // Utility
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(ProjectBasename + ".data", ProjectBasename + ".datagz")));                            // DATA file
            CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(ProjectBasename + ".data.js", ProjectBasename + ".data.jsgz")));                      // DATA file .js driver (emscripten)
            if (File.Exists(ProjectBasename + ".UE4.js"))
            {
                CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(ProjectBasename + ".UE4.js", ProjectBasename + ".UE4.jsgz")));                            // UE4 js
            }
            if (File.Exists(ProjectBasename + ".css"))
            {
                CompressionTasks.Add(Task.Factory.StartNew(() => CompressFile(ProjectBasename + ".css", ProjectBasename + ".cssgz")));                                          // UE4 css
            }
            Task.WaitAll(CompressionTasks.ToArray());
        }
        else
        {
            LogInformation("Build configuration is " + ProjectConfiguration + ", so not compressing. Build Shipping configuration to compress files to save space.");

            // nuke old compressed files to prevent using stale files
            File.Delete(UE4GameBasename + ".wasmgz");
            File.Delete(UE4GameBasename + ".jsgz");
            File.Delete(UE4GameBasename + ".js.symbolsgz");
            File.Delete(PackagePath + "/Utility.jsgz");
            File.Delete(ProjectBasename + ".datagz");
            File.Delete(ProjectBasename + ".data.jsgz");
            File.Delete(ProjectBasename + ".UE4.jsgz");
            File.Delete(ProjectBasename + ".cssgz");
        }

        File.Copy(CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/HTML5LaunchHelper.exe"), CombinePaths(PackagePath, "HTML5LaunchHelper.exe"), true);
//		Task.WaitAll(CompressionTasks);
        PrintRunTime();
    }
Example #8
0
    public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
    {
        Log("Package {0}", Params.RawProjectPath);

        Log("Setting Emscripten SDK for packaging..");
        HTML5SDKInfo.SetupEmscriptenTemp();
        HTML5SDKInfo.SetUpEmscriptenConfigFile();

        string PackagePath = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Binaries", "HTML5");

        if (!Directory.Exists(PackagePath))
        {
            Directory.CreateDirectory(PackagePath);
        }
        string FinalDataLocation = Path.Combine(PackagePath, Params.ShortProjectName) + ".data";

        var ConfigCache = new UnrealBuildTool.ConfigCacheIni(UnrealTargetPlatform.HTML5, "Engine", Path.GetDirectoryName(Params.RawProjectPath.FullName), CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine"));

        if (HTMLPakAutomation.CanCreateMapPaks(Params))
        {
            HTMLPakAutomation PakAutomation = new HTMLPakAutomation(Params, SC);

            // Create Necessary Paks.
            PakAutomation.CreateEnginePak();
            PakAutomation.CreateGamePak();
            PakAutomation.CreateContentDirectoryPak();

            // Create Emscripten Package from Necessary Paks. - This will be the VFS.
            PakAutomation.CreateEmscriptenDataPackage(PackagePath, FinalDataLocation);

            // Create All Map Paks which  will be downloaded on the fly.
            PakAutomation.CreateMapPak();

            // Create Delta Paks if setup.
            List <string> Paks = new List <string>();
            ConfigCache.GetArray("/Script/HTML5PlatformEditor.HTML5TargetSettings", "LevelTransitions", out Paks);

            if (Paks != null)
            {
                foreach (var Pak in Paks)
                {
                    var    Matched = Regex.Matches(Pak, "\"[^\"]+\"", RegexOptions.IgnoreCase);
                    string MapFrom = Path.GetFileNameWithoutExtension(Matched[0].ToString().Replace("\"", ""));
                    string MapTo   = Path.GetFileNameWithoutExtension(Matched[1].ToString().Replace("\"", ""));
                    PakAutomation.CreateDeltaMapPaks(MapFrom, MapTo);
                }
            }
        }
        else
        {
            // we need to operate in the root
            string PythonPath   = HTML5SDKInfo.Python();
            string PackagerPath = HTML5SDKInfo.EmscriptenPackager();

            using (new ScopedEnvVar("EM_CONFIG", HTML5SDKInfo.DOT_EMSCRIPTEN))
            {
                using (new PushedDirectory(Path.Combine(Params.BaseStageDirectory, "HTML5")))
                {
                    string CmdLine = string.Format("\"{0}\" \"{1}\" --preload . --js-output=\"{1}.js\"", PackagerPath, FinalDataLocation);
                    RunAndLog(CmdEnv, PythonPath, CmdLine);
                }
            }
        }

        // copy the "Executable" to the package directory
        string GameExe = Path.GetFileNameWithoutExtension(Params.ProjectGameExeFilename);

        if (Params.ClientConfigsToBuild[0].ToString() != "Development")
        {
            GameExe += "-HTML5-" + Params.ClientConfigsToBuild[0].ToString();
        }
        GameExe += ".js";

        // ensure the ue4game binary exists, if applicable
        string FullGameExePath = Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe);

        if (!SC.IsCodeBasedProject && !FileExists_NoExceptions(FullGameExePath))
        {
            Log("Failed to find game application " + FullGameExePath);
            throw new AutomationException(ExitCode.Error_MissingExecutable, "Stage Failed. Could not find application {0}. You may need to build the UE4 project with your target configuration and platform.", FullGameExePath);
        }

        if (Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe) != Path.Combine(PackagePath, GameExe))
        {
            File.Copy(Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe), Path.Combine(PackagePath, GameExe), true);
            File.Copy(Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe) + ".mem", Path.Combine(PackagePath, GameExe) + ".mem", true);
            File.Copy(Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe) + ".symbols", Path.Combine(PackagePath, GameExe) + ".symbols", true);
        }

        File.SetAttributes(Path.Combine(PackagePath, GameExe), FileAttributes.Normal);
        File.SetAttributes(Path.Combine(PackagePath, GameExe) + ".mem", FileAttributes.Normal);
        File.SetAttributes(Path.Combine(PackagePath, GameExe) + ".symbols", FileAttributes.Normal);


        // put the HTML file to the package directory
        string TemplateFileName = "GameX.html.template";
        string TemplateFile     = Path.Combine(CombinePaths(CmdEnv.LocalRoot, "Engine"), "Build", "HTML5", TemplateFileName);
        string OutputFile       = Path.Combine(PackagePath, (Params.ClientConfigsToBuild[0].ToString() != "Development" ? (Params.ShortProjectName + "-HTML5-" + Params.ClientConfigsToBuild[0].ToString()) : Params.ShortProjectName)) + ".html";

        // find Heap Size.
        ulong HeapSize;

        int ConfigHeapSize = 0;
        // Valuer set by Editor UI
        var bGotHeapSize = ConfigCache.GetInt32("/Script/HTML5PlatformEditor.HTML5TargetSettings", "HeapSize" + Params.ClientConfigsToBuild[0].ToString(), out ConfigHeapSize);

        // Fallback if the previous method failed
        if (!bGotHeapSize && !ConfigCache.GetInt32("/Script/BuildSettings.BuildSettings", "HeapSize" + Params.ClientConfigsToBuild[0].ToString(), out ConfigHeapSize))         // in Megs.
        {
            // we couldn't find a per config heap size, look for a common one.
            if (!ConfigCache.GetInt32("/Script/BuildSettings.BuildSettings", "HeapSize", out ConfigHeapSize))
            {
                ConfigHeapSize = Params.IsCodeBasedProject ? 1024 : 512;
                Log("Could not find Heap Size setting in .ini for Client config {0}", Params.ClientConfigsToBuild[0].ToString());
            }
        }

        HeapSize = (ulong)ConfigHeapSize * 1024L * 1024L;         // convert to bytes.
        Log("Setting Heap size to {0} Mb ", ConfigHeapSize);


        GenerateFileFromTemplate(TemplateFile, OutputFile, Params.ShortProjectName, Params.ClientConfigsToBuild[0].ToString(), Params.StageCommandline, !Params.IsCodeBasedProject, HeapSize);

        string MacBashTemplateFile = Path.Combine(CombinePaths(CmdEnv.LocalRoot, "Engine"), "Build", "HTML5", "RunMacHTML5LaunchHelper.command.template");
        string MacBashOutputFile   = Path.Combine(PackagePath, "RunMacHTML5LaunchHelper.command");
        string MonoPath            = Path.Combine(CombinePaths(CmdEnv.LocalRoot, "Engine"), "Build", "BatchFiles", "Mac", "SetupMono.sh");

        GenerateMacCommandFromTemplate(MacBashTemplateFile, MacBashOutputFile, MonoPath);

        string htaccessTemplate = Path.Combine(CombinePaths(CmdEnv.LocalRoot, "Engine"), "Build", "HTML5", "htaccess.template");
        string htaccesspath     = Path.Combine(PackagePath, ".htaccess");

        if (File.Exists(htaccesspath))
        {
            FileAttributes attributes = File.GetAttributes(htaccesspath);
            if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
            {
                attributes &= ~FileAttributes.ReadOnly;
                File.SetAttributes(htaccesspath, attributes);
            }
        }
        File.Copy(htaccessTemplate, htaccesspath, true);

        string JSDir  = Path.Combine(CombinePaths(CmdEnv.LocalRoot, "Engine"), "Build", "HTML5");
        string OutDir = PackagePath;

        // Gather utlity .js files and combine into one file
        string[] UtilityJavaScriptFiles = Directory.GetFiles(JSDir, "*.js");

        string DestinationFile = OutDir + "/Utility.js";

        File.Delete(DestinationFile);
        foreach (var UtilityFile in UtilityJavaScriptFiles)
        {
            string Data = File.ReadAllText(UtilityFile);
            File.AppendAllText(DestinationFile, Data);
        }

        // Compress all files. These are independent tasks which can be threaded.
        Task[] CompressionTasks = new Task[6];
        //data file.
        CompressionTasks[0] = Task.Factory.StartNew(() => CompressFile(FinalDataLocation, FinalDataLocation + "gz"));
        // data file .js driver.
        CompressionTasks[1] = Task.Factory.StartNew(() => CompressFile(FinalDataLocation + ".js", FinalDataLocation + ".jsgz"));
        // main js.
        CompressionTasks[2] = Task.Factory.StartNew(() => CompressFile(Path.Combine(PackagePath, GameExe), Path.Combine(PackagePath, GameExe) + "gz"));
        // mem init file.
        CompressionTasks[3] = Task.Factory.StartNew(() => CompressFile(Path.Combine(PackagePath, GameExe) + ".mem", Path.Combine(PackagePath, GameExe) + ".memgz"));
        // symbols file.
        CompressionTasks[4] = Task.Factory.StartNew(() => CompressFile(Path.Combine(PackagePath, GameExe) + ".symbols", Path.Combine(PackagePath, GameExe) + ".symbolsgz"));
        // Utility
        CompressionTasks[5] = Task.Factory.StartNew(() => CompressFile(OutDir + "/Utility.js", OutDir + "/Utility.jsgz"));

        File.Copy(CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/HTML5LaunchHelper.exe"), CombinePaths(OutDir, "HTML5LaunchHelper.exe"), true);
        Task.WaitAll(CompressionTasks);
        PrintRunTime();
    }
Example #9
0
    public override ProcessResult RunClient(ERunOptions ClientRunFlags, string ClientApp, string ClientCmdLine, ProjectParams Params)
    {
        // look for browser
        var ConfigCache = new UnrealBuildTool.ConfigCacheIni(UnrealTargetPlatform.HTML5, "Engine", Path.GetDirectoryName(Params.RawProjectPath), CombinePaths(CmdEnv.LocalRoot, "Engine"));

        string DeviceSection;

        if (Utils.IsRunningOnMono)
        {
            DeviceSection = "HTML5DevicesMac";
        }
        else
        {
            DeviceSection = "HTML5DevicesWindows";
        }

        string browserPath;
        string DeviceName = Params.Device.Split('@')[1];

        DeviceName = DeviceName.Substring(0, DeviceName.LastIndexOf(" on "));
        bool ok = ConfigCache.GetString(DeviceSection, DeviceName, out browserPath);

        if (!ok)
        {
            throw new System.Exception("Incorrect browser configuration in HTML5Engine.ini ");
        }

        // open the webpage
        string directory = Path.GetDirectoryName(ClientApp);
        string url       = Path.GetFileName(ClientApp) + ".html";
        // Are we running via cook on the fly server?
        // find our http url - This is awkward because RunClient doesn't have real information that NFS is running or not.
        bool IsCookOnTheFly = false;

        // 9/24/2014 @fixme - All this is convoluted, clean up.
        // looks like cookonthefly commandline stopped adding protocol or the port :/ hard coding to DEFAULT_TCP_FILE_SERVING_PORT+1 (DEFAULT_HTTP_FILE_SERVING_PORT)
        // This will fail if the NFS server is started with a different port - we need to modify driver .cs script to pass in IP/Port data correctly.

        if (ClientCmdLine.Contains("filehostip"))
        {
            IsCookOnTheFly = true;
            url            = "http://127.0.0.1:41898/" + url;
        }

        if (IsCookOnTheFly)
        {
            url += "?cookonthefly=true";
        }
        else
        {
            url = "http://127.0.0.1:8000/" + url;
            // this will be killed UBT instances dies.
            string input = String.Format(" -m SimpleHTTPServer 8000");

            string        PythonPath = HTML5SDKInfo.PythonPath();
            ProcessResult Result     = ProcessManager.CreateProcess(PythonPath, true, "html5server.log");
            Result.ProcessObject.StartInfo.FileName               = PythonPath;
            Result.ProcessObject.StartInfo.UseShellExecute        = false;
            Result.ProcessObject.StartInfo.RedirectStandardOutput = true;
            Result.ProcessObject.StartInfo.RedirectStandardInput  = true;
            Result.ProcessObject.StartInfo.WorkingDirectory       = directory;
            Result.ProcessObject.StartInfo.Arguments              = input;
            Result.ProcessObject.Start();

            Result.ProcessObject.OutputDataReceived += delegate(object sender, System.Diagnostics.DataReceivedEventArgs e)
            {
                System.Console.WriteLine(e.Data);
            };

            System.Console.WriteLine("Starting Browser Process");


            // safari specific hack.
            string argument = url;
            if (browserPath.Contains("Safari") && Utils.IsRunningOnMono)
            {
                argument = "";
            }

            // Chrome issue. Firefox may work like this in the future
            bool bBrowserWillSpawnProcess = browserPath.Contains("chrome") || (browserPath.Contains("Google Chrome") && Utils.IsRunningOnMono);

            ProcessResult SubProcess    = null;
            ProcessResult ClientProcess = Run(browserPath, argument, null, ClientRunFlags | ERunOptions.NoWaitForExit);
            var           ProcStartTime = ClientProcess.ProcessObject.StartTime;
            var           ProcName      = ClientProcess.ProcessObject.ProcessName;

            ClientProcess.ProcessObject.EnableRaisingEvents = true;
            ClientProcess.ProcessObject.Exited += delegate(System.Object o, System.EventArgs e)
            {
                System.Console.WriteLine("Browser Process Ended (PID={0})", ClientProcess.ProcessObject.Id);
                var bFoundChildProcess = true;
                if (bBrowserWillSpawnProcess)
                {
                    bFoundChildProcess = false;
                    // Chrome spawns a process from the tab it opens and then lets the process we spawned die, so
                    // catch that process and attach to that instead.
                    var CurrentProcesses = Process.GetProcesses();
                    foreach (var item in CurrentProcesses)
                    {
                        if (item.Id != ClientProcess.ProcessObject.Id && item.ProcessName == ProcName && item.StartTime >= ProcStartTime && item.StartTime <= ClientProcess.ProcessObject.ExitTime)
                        {
                            var PID = item.Id;
                            System.Console.WriteLine("Found Process {0} with PID {1} which started at {2}. Waiting on that process to end.", item.ProcessName, item.Id, item.StartTime.ToString());
                            SubProcess = new ProcessResult(item.ProcessName, item, true, item.ProcessName);
                            item.EnableRaisingEvents = true;
                            item.Exited += delegate(System.Object o2, System.EventArgs e2)
                            {
                                System.Console.WriteLine("Browser Process Ended (PID={0}) - Killing Webserver", PID);
                                Result.ProcessObject.StandardInput.Close();
                                Result.ProcessObject.Kill();
                            };
                            bFoundChildProcess = true;
                        }
                    }
                }

                if (!bFoundChildProcess)
                {
                    System.Console.WriteLine("- Killing Webserver", ClientProcess.ProcessObject.Id);
                    Result.ProcessObject.StandardInput.Close();
                    Result.ProcessObject.Kill();
                }
            };

            if (bBrowserWillSpawnProcess)
            {
                //Wait for it to do so...
                ClientProcess.ProcessObject.WaitForExit();
                ClientProcess = SubProcess;
            }

            // safari needs a hack.
            // http://superuser.com/questions/689315/run-safari-from-terminal-with-given-url-address-without-open-command

            if (browserPath.Contains("Safari") && Utils.IsRunningOnMono)
            {
                // ClientProcess.ProcessObject.WaitForInputIdle ();
                Thread.Sleep(2000);
                Process.Start("/usr/bin/osascript", " -e 'tell application \"Safari\" to open location \"" + url + "\"'");
            }

            return(ClientProcess);
        }

        System.Console.WriteLine("Browser Path " + browserPath);
        ProcessResult BrowserProcess = Run(browserPath, url, null, ClientRunFlags | ERunOptions.NoWaitForExit);

        return(BrowserProcess);
    }
Example #10
0
    public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
    {
        Log("Package {0}", Params.RawProjectPath);

        string PackagePath = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath), "Binaries", "HTML5");

        if (!Directory.Exists(PackagePath))
        {
            Directory.CreateDirectory(PackagePath);
        }
        string FinalDataLocation = Path.Combine(PackagePath, Params.ShortProjectName) + ".data";

        // we need to operate in the root
        using (new PushedDirectory(Path.Combine(Params.BaseStageDirectory, "HTML5")))
        {
            string PythonPath   = HTML5SDKInfo.PythonPath();
            string PackagerPath = HTML5SDKInfo.EmscriptenPackager();

            string CmdLine = string.Format("\"{0}\" \"{1}\" --preload . --js-output=\"{1}.js\"", PackagerPath, FinalDataLocation);
            RunAndLog(CmdEnv, PythonPath, CmdLine);
        }

        // copy the "Executable" to the package directory
        string GameExe = Path.GetFileNameWithoutExtension(Params.ProjectGameExeFilename);

        if (Params.ClientConfigsToBuild[0].ToString() != "Development")
        {
            GameExe += "-HTML5-" + Params.ClientConfigsToBuild[0].ToString();
        }
        GameExe += ".js";
        if (Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe) != Path.Combine(PackagePath, GameExe))
        {
            File.Copy(Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe), Path.Combine(PackagePath, GameExe), true);
            File.Copy(Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe) + ".mem", Path.Combine(PackagePath, GameExe) + ".mem", true);
        }
        File.SetAttributes(Path.Combine(PackagePath, GameExe), FileAttributes.Normal);
        File.SetAttributes(Path.Combine(PackagePath, GameExe) + ".mem", FileAttributes.Normal);

        // put the HTML file to the package directory
        string TemplateFile = Path.Combine(CombinePaths(CmdEnv.LocalRoot, "Engine"), "Build", "HTML5", "Game.html.template");
        string OutputFile   = Path.Combine(PackagePath, (Params.ClientConfigsToBuild[0].ToString() != "Development" ? (Params.ShortProjectName + "-HTML5-" + Params.ClientConfigsToBuild[0].ToString()) : Params.ShortProjectName)) + ".html";

        // find Heap Size.
        ulong HeapSize;
        var   ConfigCache = new UnrealBuildTool.ConfigCacheIni(UnrealTargetPlatform.HTML5, "Engine", Path.GetDirectoryName(Params.RawProjectPath), CombinePaths(CmdEnv.LocalRoot, "Engine"));

        int ConfigHeapSize;

        if (!ConfigCache.GetInt32("BuildSettings", "HeapSize" + Params.ClientConfigsToBuild[0].ToString(), out ConfigHeapSize))     // in Megs.
        {
            // we couldn't find a per config heap size, look for a common one.
            if (!ConfigCache.GetInt32("BuildSettings", "HeapSize", out ConfigHeapSize))
            {
                ConfigHeapSize = Params.IsCodeBasedProject ? 1024 : 512;
                Log("Could not find Heap Size setting in .ini for Client config {0}", Params.ClientConfigsToBuild[0].ToString());
            }
        }

        HeapSize = (ulong)ConfigHeapSize * 1024L * 1024L;     // convert to bytes.
        Log("Setting Heap size to {0} Mb ", ConfigHeapSize);


        GenerateFileFromTemplate(TemplateFile, OutputFile, Params.ShortProjectName, Params.ClientConfigsToBuild[0].ToString(), Params.StageCommandline, !Params.IsCodeBasedProject, HeapSize);

        // copy the jstorage files to the binaries directory
        string JSDir  = Path.Combine(CombinePaths(CmdEnv.LocalRoot, "Engine"), "Build", "HTML5");
        string OutDir = PackagePath;

        File.Copy(JSDir + "/json2.js", OutDir + "/json2.js", true);
        File.SetAttributes(OutDir + "/json2.js", FileAttributes.Normal);
        File.Copy(JSDir + "/jstorage.js", OutDir + "/jstorage.js", true);
        File.SetAttributes(OutDir + "/jstorage.js", FileAttributes.Normal);
        File.Copy(JSDir + "/moz_binarystring.js", OutDir + "/moz_binarystring.js", true);
        File.SetAttributes(OutDir + "/moz_binarystring.js", FileAttributes.Normal);
        PrintRunTime();
    }
    protected void GenerateFileFromTemplate(string InTemplateFile, string InOutputFile, ProjectParams Params, DeploymentContext SC, ConfigHierarchy ConfigCache)
    {
        bool   IsContentOnly        = !Params.IsCodeBasedProject;
        string ProjectConfiguration = Params.ClientConfigsToBuild[0].ToString();

        string UE4GameName = IsContentOnly ? "UE4Game" : (Params.HasClientCookedTargets ? Params.ClientCookedTargets[0] : Params.ShortProjectName);
        string ProjectName = Params.ShortProjectName;

        if (ProjectConfiguration != "Development")
        {
            UE4GameName += "-HTML5-" + ProjectConfiguration;
            ProjectName += "-HTML5-" + ProjectConfiguration;
        }

        string CanvasScaleMode;

        ConfigCache.GetString("/Script/HTML5PlatformEditor.HTML5TargetSettings", "CanvasScalingMode", out CanvasScaleMode);

        StringBuilder outputContents = new StringBuilder();

        using (StreamReader reader = new StreamReader(InTemplateFile))
        {
            string LineStr = null;
            while (reader.Peek() != -1)
            {
                LineStr = reader.ReadLine();
                if (LineStr.Contains("%TIMESTAMP%"))
                {
                    string TimeStamp = DateTime.UtcNow.ToString("yyyyMMddHHmm");
                    LineStr = LineStr.Replace("%TIMESTAMP%", TimeStamp);
                }

                if (LineStr.Contains("%SHORTNAME%"))
                {
                    LineStr = LineStr.Replace("%SHORTNAME%", Params.ShortProjectName);
                }

                if (LineStr.Contains("%UE4GAMENAME%"))
                {
                    LineStr = LineStr.Replace("%UE4GAMENAME%", UE4GameName);
                }

                if (LineStr.Contains("%PROJECTNAME%"))
                {
                    LineStr = LineStr.Replace("%PROJECTNAME%", ProjectName);
                }

                if (LineStr.Contains("%SERVE_COMPRESSED%"))
                {
                    LineStr = LineStr.Replace("%SERVE_COMPRESSED%", Compressed ? "true" : "false");
                }

                if (LineStr.Contains("%DISABLE_INDEXEDDB%"))
                {
                    LineStr = LineStr.Replace("%DISABLE_INDEXEDDB%",
                                              enableIndexedDB ? "" : "enableReadFromIndexedDB = false;\nenableWriteToIndexedDB = false;");
                }

                if (LineStr.Contains("%UE4CMDLINE%"))
                {
                    string ArgumentString = "'../../../" + Params.ShortProjectName + "/" + Params.ShortProjectName + ".uproject',";

                    if (Params.MapToRun.Length > 0)
                    {
                        ArgumentString += "'" + Params.MapToRun + "',";
                    }
                    ArgumentString += "'-stdout',";                     // suppress double printing to console.log
                    LineStr         = LineStr.Replace("%UE4CMDLINE%", ArgumentString);
                }

                if (LineStr.Contains("%CANVASSCALEMODE%"))
                {
                    string mode = "2 /*ASPECT*/";                     // default
                    if (CanvasScaleMode.Equals("stretch", StringComparison.InvariantCultureIgnoreCase))
                    {
                        mode = "1 /*STRETCH*/";
                    }
                    else if (CanvasScaleMode.Equals("fixed", StringComparison.InvariantCultureIgnoreCase))
                    {
                        mode = "3 /*FIXED*/";
                    }
                    LineStr = LineStr.Replace("%CANVASSCALEMODE%", mode);
                }

                if (LineStr.Contains("%MULTITHREADED%"))
                {
                    LineStr = LineStr.Replace("%MULTITHREADED%", enableMultithreading ? "true" : "false");
                }

                if (LineStr.Contains("%OFFSCREENCANVAS%"))
                {
                    LineStr = LineStr.Replace("%OFFSCREENCANVAS%", bMultithreading_UseOffscreenCanvas ? "true" : "false");
                }

                if (LineStr.Contains("%EMSDK_VERSION%"))
                {
                    string escpath = HTML5SDKInfo.EMSCRIPTEN_ROOT.Replace("\\", "/");
                    LineStr = LineStr.Replace("%EMSDK_VERSION%", (ProjectConfiguration == "Shipping") ? HTML5SDKInfo.EmscriptenVersion() : escpath);
                }

                if (LineStr.Contains("%EMSDK_CONFIG%"))
                {
                    string escpath = HTML5SDKInfo.DOT_EMSCRIPTEN.Replace("\\", "/");
                    LineStr = LineStr.Replace("%EMSDK_CONFIG%", (ProjectConfiguration == "Shipping") ? "" : escpath);
                }

                outputContents.AppendLine(LineStr);
            }
        }

        if (outputContents.Length > 0)
        {
            // Save the file
            try
            {
                Directory.CreateDirectory(Path.GetDirectoryName(InOutputFile));
                File.WriteAllText(InOutputFile, outputContents.ToString(), Encoding.UTF8);
            }
            catch (Exception)
            {
                // Unable to write to the project file.
            }
        }
    }
    public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
    {
        Log("Package {0}", Params.RawProjectPath);

        string PackagePath = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath), "Binaries", "HTML5");

        if (!Directory.Exists(PackagePath))
        {
            Directory.CreateDirectory(PackagePath);
        }
        string FinalDataLocation = Path.Combine(PackagePath, Params.ShortProjectName) + ".data";

        var ConfigCache = new UnrealBuildTool.ConfigCacheIni(UnrealTargetPlatform.HTML5, "Engine", Path.GetDirectoryName(Params.RawProjectPath), CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine"));

        if (HTMLPakAutomation.CanCreateMapPaks(Params))
        {
            HTMLPakAutomation PakAutomation = new HTMLPakAutomation(Params, SC);

            // Create Necessary Paks.
            PakAutomation.CreateEnginePak();
            PakAutomation.CreateGamePak();
            PakAutomation.CreateContentDirectoryPak();

            // Create Emscripten Package from Necessary Paks. - This will be the VFS.
            PakAutomation.CreateEmscriptenDataPackage(PackagePath, FinalDataLocation);

            // Create All Map Paks which  will be downloaded on the fly.
            PakAutomation.CreateMapPak();

            // Create Delta Paks if setup.
            List <string> Paks = new List <string>();
            ConfigCache.GetArray("/Script/HTML5PlatformEditor.HTML5TargetSettings", "LevelTransitions", out Paks);

            if (Paks != null)
            {
                foreach (var Pak in Paks)
                {
                    var    Matched = Regex.Matches(Pak, "\"[^\"]+\"", RegexOptions.IgnoreCase);
                    string MapFrom = Path.GetFileNameWithoutExtension(Matched[0].ToString().Replace("\"", ""));
                    string MapTo   = Path.GetFileNameWithoutExtension(Matched[1].ToString().Replace("\"", ""));
                    PakAutomation.CreateDeltaMapPaks(MapFrom, MapTo);
                }
            }
        }
        else
        {
            // we need to operate in the root
            using (new PushedDirectory(Path.Combine(Params.BaseStageDirectory, "HTML5")))
            {
                string PythonPath   = HTML5SDKInfo.PythonPath();
                string PackagerPath = HTML5SDKInfo.EmscriptenPackager();

                string CmdLine = string.Format("\"{0}\" \"{1}\" --preload . --js-output=\"{1}.js\"", PackagerPath, FinalDataLocation);
                RunAndLog(CmdEnv, PythonPath, CmdLine);
            }
        }

        // copy the "Executable" to the package directory
        string GameExe = Path.GetFileNameWithoutExtension(Params.ProjectGameExeFilename);

        if (Params.ClientConfigsToBuild[0].ToString() != "Development")
        {
            GameExe += "-HTML5-" + Params.ClientConfigsToBuild[0].ToString();
        }
        GameExe += ".js";

        // ensure the ue4game binary exists, if applicable
        string FullGameExePath = Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe);

        if (!SC.IsCodeBasedProject && !FileExists_NoExceptions(FullGameExePath))
        {
            Log("Failed to find game application " + FullGameExePath);
            AutomationTool.ErrorReporter.Error("Stage Failed.", (int)AutomationTool.ErrorCodes.Error_MissingExecutable);
            throw new AutomationException("Could not find application {0}. You may need to build the UE4 project with your target configuration and platform.", FullGameExePath);
        }

        if (Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe) != Path.Combine(PackagePath, GameExe))
        {
            File.Copy(Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe), Path.Combine(PackagePath, GameExe), true);
            File.Copy(Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe) + ".mem", Path.Combine(PackagePath, GameExe) + ".mem", true);
            File.Copy(Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), GameExe) + ".symbols", Path.Combine(PackagePath, GameExe) + ".symbols", true);
        }
        File.SetAttributes(Path.Combine(PackagePath, GameExe), FileAttributes.Normal);
        File.SetAttributes(Path.Combine(PackagePath, GameExe) + ".mem", FileAttributes.Normal);
        File.SetAttributes(Path.Combine(PackagePath, GameExe) + ".symbols", FileAttributes.Normal);


        // put the HTML file to the package directory

        bool UseExperimentalTemplate = false;

        ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "UseExperimentalTemplate", out UseExperimentalTemplate);
        string TemplateFileName = UseExperimentalTemplate ? "GameX.html.template" : "Game.html.template";

        string TemplateFile = Path.Combine(CombinePaths(CmdEnv.LocalRoot, "Engine"), "Build", "HTML5", TemplateFileName);
        string OutputFile   = Path.Combine(PackagePath, (Params.ClientConfigsToBuild[0].ToString() != "Development" ? (Params.ShortProjectName + "-HTML5-" + Params.ClientConfigsToBuild[0].ToString()) : Params.ShortProjectName)) + ".html";

        // find Heap Size.
        ulong HeapSize;

        int ConfigHeapSize = 0;
        // Valuer set by Editor UI
        var bGotHeapSize = ConfigCache.GetInt32("/Script/HTML5PlatformEditor.HTML5TargetSettings", "HeapSize" + Params.ClientConfigsToBuild[0].ToString(), out ConfigHeapSize);

        // Fallback if the previous method failed
        if (!bGotHeapSize && !ConfigCache.GetInt32("BuildSettings", "HeapSize" + Params.ClientConfigsToBuild[0].ToString(), out ConfigHeapSize)) // in Megs.
        {
            // we couldn't find a per config heap size, look for a common one.
            if (!ConfigCache.GetInt32("BuildSettings", "HeapSize", out ConfigHeapSize))
            {
                ConfigHeapSize = Params.IsCodeBasedProject ? 1024 : 512;
                Log("Could not find Heap Size setting in .ini for Client config {0}", Params.ClientConfigsToBuild[0].ToString());
            }
        }

        HeapSize = (ulong)ConfigHeapSize * 1024L * 1024L; // convert to bytes.
        Log("Setting Heap size to {0} Mb ", ConfigHeapSize);


        GenerateFileFromTemplate(TemplateFile, OutputFile, Params.ShortProjectName, Params.ClientConfigsToBuild[0].ToString(), Params.StageCommandline, !Params.IsCodeBasedProject, HeapSize);

        // copy the jstorage files to the binaries directory
        string JSDir  = Path.Combine(CombinePaths(CmdEnv.LocalRoot, "Engine"), "Build", "HTML5");
        string OutDir = PackagePath;

        File.Copy(JSDir + "/json2.js", OutDir + "/json2.js", true);
        File.SetAttributes(OutDir + "/json2.js", FileAttributes.Normal);
        File.Copy(JSDir + "/jStorage.js", OutDir + "/jStorage.js", true);
        File.SetAttributes(OutDir + "/jStorage.js", FileAttributes.Normal);
        File.Copy(JSDir + "/moz_binarystring.js", OutDir + "/moz_binarystring.js", true);
        File.SetAttributes(OutDir + "/moz_binarystring.js", FileAttributes.Normal);
        PrintRunTime();
    }