예제 #1
0
            void ICustomVisit <UTinyEnum.Reference> .CustomVisit(UTinyEnum.Reference value)
            {
                if (!IsListItem)
                {
                    return;
                }

                var type       = value.Type.Dereference(VisitorContext.Registry);
                var normalized = ExportEnumAsValue
                    ? (type.DefaultValue as UTinyObject)?[value.Name]
                    : $"{UTinyBuildPipeline.GetJsTypeName(type)}.{value.Name}";

                VisitorContext.Writer.Line($"{Path}[{ListIndex}] = {normalized};");
            }
예제 #2
0
        public UTinyBuildReport GetBuildReport()
        {
            m_IdCounter = 0;

            var buildDir = UTinyBuildPipeline.GetBuildDirectory(UTinyEditorApplication.Project, UTinyPlatform.HTML5, UTinyEditorApplication.EditorContext.Workspace.BuildConfiguration);
            var jsonFile = new FileInfo(Path.Combine(buildDir, "build-report.json"));

            if (jsonFile.Exists)
            {
                var json = File.ReadAllText(jsonFile.FullName);
                return(UTinyBuildReport.FromJson(json));
            }

            return(null);
        }
        /// <summary>
        /// Packages the runtime to `runtime.js`
        /// </summary>
        private static void PackageRuntime(UTinyBuildOptions options, UTinyBuildResults results)
        {
            string buildFolderName;

            switch (options.Configuration)
            {
            case UTinyBuildConfiguration.Debug:
                buildFolderName = "build-js-debug";
                break;

            case UTinyBuildConfiguration.Development:
            case UTinyBuildConfiguration.Release:
                buildFolderName = "build-js-release";
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            var runtimeVariant = UTinyBuildPipeline.GetJsRuntimeVariant(options);
            var distFolder     = UTinyBuildPipeline.GetRuntimeDistFolder();
            var runtimeFiles   = new DirectoryInfo(Path.Combine(distFolder.FullName, buildFolderName + "/runtime")).GetFiles(runtimeVariant + "*", SearchOption.TopDirectoryOnly);

            var reportRuntime = results.BuildReport.AddChild(UTinyBuildReport.RuntimeNode);

            foreach (var runtimeFile in runtimeFiles)
            {
                if (runtimeFile.Name.EndsWith(".js.symbols") || runtimeFile.Name.EndsWith(".js.map") || runtimeFile.Name.EndsWith(".dll"))
                {
                    continue;
                }
                var destPath = Path.Combine(results.BinaryFolder.FullName, $"runtime{runtimeFile.Extension}");

                if (runtimeFile.Name == "GeminiRuntime.js")
                {
                    var dependencies = options.Project.Module.Dereference(options.Project.Registry).EnumerateDependencies();
                    var regex        = new System.Text.RegularExpressions.Regex(@"\/\*if\(([\s\S]*?)\)\*\/([\s\S]*?)\/\*endif\(([\s\S]*?)\)\*\/");
                    var runtime      = File.ReadAllText(runtimeFile.FullName);
                    runtime = regex.Replace(runtime, match => match.Groups[match.Groups[1].Value.Split('|').Any(module => dependencies.WithName("UTiny." + module).Any()) ? 2 : 3].Value);
                    File.WriteAllText(destPath, runtime);
                    reportRuntime.AddChild(new FileInfo(destPath));
                }
                else
                {
                    reportRuntime.AddChild(runtimeFile.CopyTo(destPath));
                }
            }
        }
예제 #4
0
            void ICustomVisit <UTinyObject> .CustomVisit(UTinyObject value)
            {
                if (!IsListItem)
                {
                    return;
                }

                var type = value.Type.Dereference(value.Registry);

                var index = ++VisitorContext.StructIndex;

                VisitorContext.Writer.Line($"var s{index} = new {UTinyBuildPipeline.GetJsTypeName(type)}();");
                value.Properties.Visit(new StructVisitor {
                    VisitorContext = VisitorContext, Path = $"s{index}"
                });
                VisitorContext.Writer.Line($"{Path}[{ListIndex}] = s{index};");
            }
        private bool HostContent(string contentDir)
        {
            StopHostingContent();

            // Setup a signal event with handlers
            string                   stdout = "", stderr = "";
            ManualResetEvent         isRunning      = new ManualResetEvent(false);
            DataReceivedEventHandler outputReceived = (sender, e) => { stdout += e.Data; isRunning.Set(); };
            DataReceivedEventHandler errorReceived  = (sender, e) => { stderr += e.Data; isRunning.Set(); };

            // Start new local http server
            var ppid            = Process.GetCurrentProcess().Id;
            var port            = UTinyEditorApplication.Project.Settings.LocalHTTPServerPort;
            var httpServerDir   = new DirectoryInfo(UTinyBuildPipeline.GetToolDirectory("httpserver"));
            var unityVersion    = InternalEditorUtility.GetUnityVersion();
            var profilerVersion = unityVersion.Major > 2018 || (unityVersion.Major == 2018 && unityVersion.Minor > 1) ? 0x20180123 : 0x20170327;

            m_LocalServerProcess = UTinyBuildUtilities.RunNodeNoWait(httpServerDir, "index.js", $"--pid {ppid} --port {port} --dir \"{contentDir}\" --profiler " + profilerVersion, outputReceived, errorReceived);
            if (m_LocalServerProcess == null)
            {
                throw new Exception("Failed to create local http server process.");
            }

            // Wait for the process to write something in either stdout or stderr
            isRunning.WaitOne(3000);

            // Check if process state is valid
            if (m_LocalServerProcess.HasExited || stderr.Length > 0)
            {
                var errorMsg = "Failed to start local http server.";
                if (stderr.Length > 0)
                {
                    errorMsg += $"\n{stderr}";
                }
                else if (stdout.Length > 0)
                {
                    errorMsg += $"\n{stdout}";
                }
                UnityEngine.Debug.LogError(errorMsg);
                return(false);
            }
            return(true);
        }
        /// <summary>
        /// Generates `wsclient.js` to handle live-linking
        /// </summary>
        private static void GenerateWebSocketClient(UTinyBuildOptions options, UTinyBuildResults results)
        {
            if (!options.Project.Settings.IncludeWSClient)
            {
                return;
            }

            // Put local http server address into the wsclient script
            var content = File.ReadAllText(Path.Combine(UTinyBuildPipeline.GetToolDirectory("wsclient"), KWebSocketClientFileName));

            content = content.Replace("{{IPADDRESS}}", UTinyServer.Instance.LocalIPAddress);

            // Write wsclient to binary dir
            var file = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KWebSocketClientFileName));

            File.WriteAllText(file.FullName, content, Encoding.UTF8);

            results.BuildReport.GetOrAddChild(UTinyBuildReport.CodeNode).AddChild(file);
        }
            public Item(FileInfo file, Object obj) :
                this(UTinyBuildPipeline.GetRelativePath(file), file?.Length ?? 0, obj)
            {
                if (file == null)
                {
                    throw new ArgumentNullException(nameof(file));
                }

                if (!file.Exists)
                {
                    throw new FileNotFoundException("file");
                }

                if (file.Exists && file.Length > 0)
                {
                    CompressedSize = GetCompressedSize(file);
                    if (CompressedSize == 0)
                    {
                        throw new Exception("GetCompressedSize(file)");
                    }
                }
            }
        /// <summary>
        /// Generates `libwebp.js` to handle WebP decompressor
        /// </summary>
        private static void GenerateWebPDecompressor(UTinyBuildOptions options, UTinyBuildResults results)
        {
            // Check if project use WebP texture format
            var module   = options.Project.Module.Dereference(options.Project.Registry);
            var webpUsed = AssetIterator.EnumerateAssets(module)
                           .Select(a => a.Object)
                           .OfType <Texture2D>()
                           .Select(t => UTinyUtility.GetAssetExportSettings(options.Project, t))
                           .OfType <UTinyTextureSettings>()
                           .Any(s => s.FormatType == TextureFormatType.WebP);

            // Warn about WebP usages
            if (options.Project.Settings.IncludeWebPDecompressor)
            {
                if (!webpUsed)
                {
                    Debug.LogWarning("This project does not uses the WebP texture format, but includes the WebP decompressor code. To reduce build size, it is recommended to disable \"Include WebP Decompressor\" in project settings.");
                }
            }
            else // WebP decompressor not included, do not copy to binary dir
            {
                if (webpUsed)
                {
                    Debug.LogWarning("This project uses the WebP texture format, but does not include the WebP decompressor code. The content will not load in browsers that do not natively support the WebP format. To ensure maximum compatibility, enable \"Include WebP Decompressor\" in project settings.");
                }
                return;
            }

            // Copy libwebp to binary dir
            var srcFile = Path.Combine(UTinyBuildPipeline.GetToolDirectory("libwebp"), KWebPDecompressorFileName);
            var dstFile = Path.Combine(results.BinaryFolder.FullName, KWebPDecompressorFileName);

            File.Copy(srcFile, dstFile);

            results.BuildReport.GetOrAddChild(UTinyBuildReport.CodeNode).AddChild(new FileInfo(dstFile));
        }
        public bool DrawLayout()
        {
            var module = m_MainModule.Dereference(m_Registry);
            var system = System.Dereference(m_Registry);

            if (null == system || system.IsRuntimeIncluded)
            {
                return(false);
            }

            m_AvailableSystems.Clear();
            m_AvailableSystems.AddRange(module.EnumerateDependencies().SystemRefs());

            m_AvailableComponentTypes.Clear();
            m_AvailableComponentTypes.AddRange(module.EnumerateDependencies().ComponentTypeRefs().Select(r => (UTinyType.Reference)r.Dereference(m_Registry)));

            EditorGUI.BeginChangeCheck();

            using (var scroll = new GUILayout.ScrollViewScope(m_Scroll, GUILayout.ExpandWidth(true)))
            {
                m_Scroll = scroll.scrollPosition;

                EditorGUI.BeginChangeCheck();
                system.Name = EditorGUILayout.DelayedTextField("Name", system.Name);
                if (EditorGUI.EndChangeCheck())
                {
                    OnRenameEnded?.Invoke(system);
                }

                using (new EditorGUILayout.HorizontalScope())
                {
                    EditorGUILayout.PrefixLabel("Description");
                    system.Documentation.Summary = EditorGUILayout.TextArea(system.Documentation.Summary, GUILayout.Height(50));
                }

                m_ExecuteAfterList.list = new List <UTinySystem.Reference>(system.ExecuteAfter);
                m_ExecuteAfterList.DoLayoutList();

                m_ExecuteBeforeList.list = new List <UTinySystem.Reference>(system.ExecuteBefore);
                m_ExecuteBeforeList.DoLayoutList();

                m_ComponentList.list = new List <UTinyType.Reference>(system.Components);
                m_ComponentList.DoLayoutList();

                system.External = EditorGUILayout.Toggle(new GUIContent("External", "Use this to define systems externally"), system.External);

                if (system.External)
                {
                    EditorGUILayout.HelpBox($"This system is assumed to be defined in any included script with the following signature", MessageType.Info);
                    var name = UTinyBuildPipeline.GetJsTypeName(system);
                    EditorGUILayout.SelectableLabel($"{name}.update = function(s,w) {{ /* ... */ }}", "TextArea");
                }
                else
                {
                    system.IncludeIterator = EditorGUILayout.Toggle("Include Iterator", system.IncludeIterator);

                    EditorGUILayout.Space();

                    using (new GUIEnabledScope(false))
                    {
                        var systemPrefix = UTinyBuildPipeline.GenerateSystemPrefix(system);
                        if (system.IncludeIterator)
                        {
                            systemPrefix += UTinyBuildPipeline.GenerateSystemIteratorPrefix(system);
                        }

                        EditorGUILayout.TextArea(systemPrefix);
                    }

                    EditorGUILayout.Space();

                    system.TextAsset = (TextAsset)EditorGUILayout.ObjectField("Source", system.TextAsset, typeof(TextAsset), false);

                    EditorGUILayout.Space();

                    if (null != system.TextAsset)
                    {
                        using (new GUIEnabledScope(false))
                        {
                            var text = system.TextAsset.text;
                            if (text.Length > kMaxChars)
                            {
                                text = text.Substring(0, kMaxChars) + "...\n\n<...etc...>";
                            }
                            GUILayout.TextArea(text);
                        }
                    }

                    EditorGUILayout.Space();

                    using (new GUIEnabledScope(false))
                    {
                        var systemSuffix = UTinyBuildPipeline.GenerateSystemSuffix(system);
                        if (system.IncludeIterator)
                        {
                            systemSuffix = UTinyBuildPipeline.GenerateSystemIteratorSuffix(system) + systemSuffix;
                        }
                        EditorGUILayout.TextArea(systemSuffix);
                    }
                }
            }

            return(EditorGUI.EndChangeCheck());
        }
예제 #10
0
        private void DoLayoutNoOpenProject()
        {
            var newProject     = false;
            var loadProject    = false;
            var installSamples = false;

            EditorGUILayout.BeginVertical();
            {
                EditorGUILayout.BeginHorizontal();
                {
                    GUILayout.FlexibleSpace();

                    GUILayout.Label($"No {UTinyConstants.ApplicationName} Project open");

                    GUILayout.FlexibleSpace();
                }
                EditorGUILayout.EndHorizontal();

                GUILayout.Space(5);

                EditorGUILayout.BeginHorizontal();
                {
                    GUILayout.FlexibleSpace();

                    if (GUILayout.Button($"New {UTinyConstants.ApplicationName} project"))
                    {
                        newProject = true;
                    }

                    if (GUILayout.Button($"Open {UTinyConstants.ApplicationName} project"))
                    {
                        loadProject = true;
                    }

                    GUILayout.FlexibleSpace();
                }
                EditorGUILayout.EndHorizontal();

                GUILayout.Space(5);

                EditorGUILayout.BeginHorizontal();
                {
                    GUILayout.FlexibleSpace();

                    if (GUILayout.Button("Import sample projects", UTinyStyles.LinkLabelStyle))
                    {
                        installSamples = true;
                    }

                    GUILayout.FlexibleSpace();
                }
                EditorGUILayout.EndHorizontal();

                GUILayout.FlexibleSpace();
            }
            EditorGUILayout.EndVertical();

            if (newProject)
            {
                NewProject();
            }

            if (loadProject)
            {
                LoadProject();
            }

            if (installSamples)
            {
                UTinyBuildPipeline.InstallSamples(true);
            }
        }
예제 #11
0
        private void DoLayoutToolbar()
        {
            GUILayout.Space(1);

            using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar))
            {
                using (var projectScope = new EditorGUILayout.HorizontalScope(GUILayout.Width(120)))
                {
                    var buttonLabel = GetProjectButtonName();

                    if (UTinyEditorApplication.IsChanged)
                    {
                        buttonLabel += " *";
                    }

                    if (GUILayout.Button(buttonLabel, EditorStyles.toolbarDropDown))
                    {
                        var menu = new GenericMenu();

                        menu.AddItem(new GUIContent("New Project"), false, NewProject);

                        menu.AddItem(new GUIContent("New Module"), false, NewModule);

                        menu.AddSeparator(string.Empty);
                        menu.AddItem(new GUIContent("Load..."), false, LoadProject);

                        menu.AddSeparator(string.Empty);

                        var save = new GUIContent("Save");
                        if (UTinyEditorApplication.Project == null)
                        {
                            menu.AddDisabledItem(save);
                        }
                        else
                        {
                            menu.AddItem(save, false, () =>
                            {
                                UTinyEditorApplication.Save();
                            });
                        }

                        var saveAs = new GUIContent("Save As...");
                        if (UTinyEditorApplication.Project == null)
                        {
                            menu.AddDisabledItem(saveAs);
                        }
                        else
                        {
                            menu.AddItem(saveAs, false, () =>
                            {
                                UTinyEditorApplication.SaveAs();
                            });
                        }

                        menu.AddSeparator(string.Empty);

                        var closeProject = new GUIContent("Close");
                        if (UTinyEditorApplication.Project == null)
                        {
                            menu.AddDisabledItem(closeProject);
                        }
                        else
                        {
                            menu.AddItem(closeProject, false, () =>
                            {
                                if (!UTinyEditorApplication.SaveChanges())
                                {
                                    return;
                                }

                                m_TabType = TabType.Settings;
                                UTinyEditorApplication.Close();
                            });
                        }

                        menu.DropDown(projectScope.rect);
                    }
                }

                GUILayout.FlexibleSpace();

                if (UTinyEditorApplication.ContextType == EditorContextType.Project)
                {
                    var project   = UTinyEditorApplication.Project;
                    var workspace = UTinyEditorApplication.EditorContext.Workspace;

                    if (null != project && null != workspace)
                    {
                        var lastBuildConfiguration = workspace.BuildConfiguration;
                        workspace.BuildConfiguration = (UTinyBuildConfiguration)EditorGUILayout.EnumPopup(workspace.BuildConfiguration, EditorStyles.toolbarDropDown, GUILayout.Width(100));
                        if (workspace.BuildConfiguration != lastBuildConfiguration)
                        {
                            UTinyEditorUtility.RepaintAllWindows();
                        }

                        if (GUILayout.Button("Export", EditorStyles.toolbarButton, GUILayout.Width(100)))
                        {
                            UTinyBuildPipeline.Export(project);
                        }

                        workspace.Preview = GUILayout.Toggle(
                            workspace.Preview,
                            new GUIContent(UTinyIcons.Export, "Toggles preview in browser."),
                            EditorStyles.toolbarButton,
                            GUILayout.Width(35));
                    }
                }
            }
        }
        /// <summary>
        /// Packages assets to `assets.js` or `Assets/*.*`
        /// </summary>
        private static void PackageAssets(UTinyBuildOptions options, UTinyBuildResults results)
        {
            var buildFolder = options.Destination;
            var binFolder   = results.BinaryFolder;

            // Export assets to the build directory
            var buildAssetsFolder = new DirectoryInfo(Path.Combine(buildFolder.FullName, "Assets"));

            buildAssetsFolder.Create();
            var export = UTinyAssetExporter.Export(options.Project, buildAssetsFolder);

            // copy assets to bin AND/OR encode assets to 'assets.js'
            var binAssetsFolder = new DirectoryInfo(Path.Combine(binFolder.FullName, "Assets"));

            binAssetsFolder.Create();

            var assetsFile = new FileInfo(Path.Combine(binFolder.FullName, KAssetsFileName));

            var writer = new UTinyCodeWriter();

            PrependGeneratedHeader(writer, options.Project.Name);

            var reportAssets     = results.BuildReport.AddChild(UTinyBuildReport.AssetsNode);
            var reportJavaScript = reportAssets.AddChild("JavaScript");

            using (var jsdoc = new UTinyJsdoc.Writer(writer))
            {
                jsdoc.Type("object");
                jsdoc.Desc("Map containing URLs for all assets.  If assets are included as base64 blobs, these will be data URLs.");
                jsdoc.Line("@example var assetUrl = UT_ASSETS[\"MyCustomAsset\"]");
            }

            long totalBase64Size = 0;

            using (writer.Scope("var UT_ASSETS ="))
            {
                var i = 0;
                foreach (var info in export)
                {
                    var reportAsset = reportAssets.AddChild(info.AssetInfo.AssetPath, 0, info.AssetInfo.Object);

                    var settings = UTinyUtility.GetAssetExportSettings(options.Project, info.AssetInfo.Object);
                    if (settings.Embedded)
                    {
                        foreach (var file in info.Exported)
                        {
                            var buffer        = File.ReadAllBytes(file.FullName);
                            var base64        = Convert.ToBase64String(buffer);
                            var fileExtension = Path.GetExtension(file.FullName).ToLower();

                            string mimeType;
                            switch (fileExtension)
                            {
                            case ".png":
                                mimeType = "image/png";
                                break;

                            case ".jpg":
                            case ".jpeg":
                                mimeType = "image/jpeg";
                                break;

                            case ".webp":
                                mimeType = "image/webp";
                                break;

                            case ".mp3":
                                mimeType = "audio/mpeg";
                                break;

                            case ".wav":
                                mimeType = "audio/wav";
                                break;

                            case ".json":
                                mimeType = "application/json";
                                break;

                            case ".ttf":
                                mimeType = "font/truetype";
                                break;

                            default:
                                Debug.LogWarningFormat("Asset {0} has unknown extension, included as text/plain in assets", file);
                                mimeType = "text/plain";
                                break;
                            }

                            var comma = i != 0 ? "," : "";
                            writer.Line($"{comma}\"{Path.GetFileNameWithoutExtension(file.Name)}\": \"data:{mimeType};base64,{base64}\"");
                            i++;

                            reportAsset.AddChild(UTinyBuildPipeline.GetRelativePath(file), Encoding.ASCII.GetBytes(base64), info.AssetInfo.Object);
                            totalBase64Size += base64.Length;

                            file.Delete();
                        }
                    }
                    else
                    {
                        foreach (var file in info.Exported)
                        {
                            var comma = i != 0 ? "," : "";
                            writer.Line($"{comma}\"{Path.GetFileNameWithoutExtension(file.Name)}\": \"Assets/{file.Name}\"");
                            i++;

                            reportAsset.AddChild(file, info.AssetInfo.Object);
                        }
                    }
                }
            }

            writer.Line();

            writer.WriteRaw("var UT_ASSETS_SETUP = ");
            {
                var registry = new UTinyRegistry();
                UTinyPersistence.LoadAllModules(registry);
                var entityGroup = UTinyAssetEntityGroupGenerator.Generate(registry, options.Project);
                EntityGroupSetupVisitor.WriteEntityGroupSetupFunction(writer, options.Project, entityGroup, false, false);
            }

            // Write `assets.js`
            File.WriteAllText(assetsFile.FullName, writer.ToString());

            reportJavaScript.Item.Size = assetsFile.Length - totalBase64Size;

            // Remaining assets are binplaced
            foreach (var info in export)
            {
                foreach (var file in info.Exported)
                {
                    if (!file.Exists)
                    {
                        // this asset has been packaged already
                        continue;
                    }

                    file.MoveTo(Path.Combine(binAssetsFolder.FullName, file.Name));
                }
            }

            // Clean up the build directory
            buildAssetsFolder.Delete(true);

            // if we have no standalone assets, cleanup
            if (binAssetsFolder.GetFiles().Length <= 0)
            {
                binAssetsFolder.Delete();
            }
        }
예제 #13
0
        public static void WriteSystem(UTinyCodeWriter writer, UTinySystem system)
        {
            using (var w = new Writer(writer))
            {
                w.Method();
                w.Desc($"System {system.Documentation.Summary}");

                if (system.Components.Count > 0)
                {
                    var sb = new StringBuilder();
                    sb.Append("Components [");
                    for (var i = 0; i < system.Components.Count; i++)
                    {
                        var componentRef = system.Components[i];
                        var component    = componentRef.Dereference(system.Registry);
                        if (null != component)
                        {
                            sb.AppendFormat(i == 0 ? "{{@link {0}}}" : ", {{@link {0}}}", UTinyBuildPipeline.GetJsTypeName(component));
                        }
                        else
                        {
                            throw new Exception($"System component is missing System=[{system.Name}] Component=[{componentRef.Name}]");
                        }
                    }
                    sb.Append("]");
                    w.Line(sb.ToString());
                }

                if (system.ExecuteAfter.Count > 0)
                {
                    var sb = new StringBuilder();
                    sb.Append("Execute After [");
                    for (var i = 0; i < system.ExecuteAfter.Count; i++)
                    {
                        var executeAfterRef = system.ExecuteAfter[i];
                        var executeAfter    = executeAfterRef.Dereference(system.Registry);

                        if (null != executeAfter)
                        {
                            sb.AppendFormat(i == 0 ? "{{@link {0}}}" : ", {{@link {0}}}", UTinyBuildPipeline.GetJsTypeName(executeAfter));
                        }
                        else
                        {
                            throw new Exception($"System reference is missing System=[{system.Name}] ExecuteAfter=[{executeAfterRef.Name}]");
                        }
                    }
                    sb.Append("]");
                    w.Line(sb.ToString());
                }

                if (system.ExecuteBefore.Count > 0)
                {
                    var sb = new StringBuilder();
                    sb.Append("Execute Before [");
                    for (var i = 0; i < system.ExecuteBefore.Count; i++)
                    {
                        var executeBeforeRef = system.ExecuteBefore[i];
                        var executeBefore    = executeBeforeRef.Dereference(system.Registry);

                        if (null != executeBefore)
                        {
                            sb.AppendFormat(i == 0 ? "{{@link {0}}}" : ", {{@link {0}}}", UTinyBuildPipeline.GetJsTypeName(executeBefore));
                        }
                        else
                        {
                            throw new Exception($"System reference is missing System=[{system.Name}] ExecuteBefore=[{executeBeforeRef.Name}]");
                        }
                    }
                    sb.Append("]");
                    w.Line(sb.ToString());
                }

                w.Param("ut.Scheduler", "sched");
                w.Param("ut.World", "world");
            }
        }
예제 #14
0
            void ICustomVisit <UTinyEnum.Reference> .CustomVisit(UTinyEnum.Reference value)
            {
                var type       = value.Type.Dereference(VisitorContext.Registry);
                var normalized = ExportEnumAsValue ? (type.DefaultValue as UTinyObject)?[value.Name] : $"{UTinyBuildPipeline.GetJsTypeName(type)}.{value.Name}";

                VisitorContext.Writer.Line($"{PropertySetter(Property.Name)}({normalized});");
            }
예제 #15
0
        public static void WriteEntityGroupSetupFunction(UTinyCodeWriter writer, UTinyProject project, UTinyEntityGroup entityGroup, bool writeEntityGroupComponent = true, bool writeEntityLayer = true)
        {
            var entityIndex    = 0;
            var entityIndexMap = new Dictionary <UTinyEntity.Reference, int>();

            using (writer.Scope("function(w)"))
            {
                entityIndexMap.Clear();

                foreach (var reference in entityGroup.Entities)
                {
                    var entity = reference.Dereference(entityGroup.Registry);
                    ++entityIndex;
                    entityIndexMap[reference] = entityIndex;
                    writer.Line($"var e{entityIndex} = w.create({EscapeJsString(entity.Name)});");
                }

                if (writeEntityGroupComponent)
                {
                    foreach (var reference in entityGroup.Entities)
                    {
                        var index = entityIndexMap[reference];
                        writer.Line($"e{index}.addComponent(this.Component);");
                    }
                }

                if (writeEntityLayer)
                {
                    foreach (var reference in entityGroup.Entities)
                    {
                        var index  = entityIndexMap[reference];
                        var entity = reference.Dereference(entityGroup.Registry);
                        writer.Line($"e{index}.addComponent({GetFullyQualifiedLayerName(LayerMask.LayerToName(entity.Layer))});");
                    }
                }

                var context = new VisitorContext
                {
                    Project        = project,
                    Module         = project.Module.Dereference(project.Registry),
                    Registry       = project.Registry,
                    Writer         = writer,
                    EntityIndexMap = entityIndexMap
                };

                entityIndex = 0;
                foreach (var reference in entityGroup.Entities)
                {
                    var entity = reference.Dereference(entityGroup.Registry);
                    ++entityIndex;

                    foreach (var component in entity.Components)
                    {
                        var type = component.Type.Dereference(component.Registry);

                        if (null == type)
                        {
                            Debug.LogError($"{UTinyConstants.ApplicationName}: Missing component type, ComponentType=[{component.Type.Name}] Entity=[{entity.Name}] Group=[{entityGroup.Name}]");
                            continue;
                        }

                        var index = ++context.ComponentIndex;
                        writer.Line($"var c{index} = e{entityIndex}.addComponent({UTinyBuildPipeline.GetJsTypeName(type)});");
                        component.Properties.Visit(new ComponentVisitor
                        {
                            VisitorContext = context,
                            Path           = $"c{index}",
                            Entity         = $"e{entityIndex}"
                        });
                    }
                }

                writer.WriteIndent();
                writer.WriteRaw("return [");

                for (var i = 0; i < entityIndex; i++)
                {
                    writer.WriteRaw(i != 0 ? $", e{i + 1}" : $"e{i + 1}");
                }

                writer.WriteRaw("];\n");
            }

            writer.Line().Line();
        }
        /// <summary>
        /// Outputs the final `index.html` file
        /// </summary>
        private static void GenerateHTML(UTinyBuildOptions options, UTinyBuildResults results)
        {
            var project = options.Project;

            var settingsFile         = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KSettingsFileName));
            var runtimeFile          = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KRuntimeFileName));
            var bindingsFile         = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KBindingsFileName));
            var assetsFile           = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KAssetsFileName));
            var entityGroupsFile     = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KEntityGroupsFileName));
            var systemsFile          = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KSystemsFileName));
            var codeFile             = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KCodeFileName));
            var mainFile             = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KMainFileName));
            var webSocketClientFile  = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KWebSocketClientFileName));
            var webpDecompressorFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KWebPDecompressorFileName));

            // nb: this writer is not HTML-friendly
            var writer = new UTinyCodeWriter()
            {
                CodeStyle = new CodeStyle()
                {
                    BeginBrace  = string.Empty,
                    EndBrace    = string.Empty,
                    BraceLayout = BraceLayout.EndOfLine,
                    Indent      = "  ",
                    NewLine     = Environment.NewLine
                }
            };

            writer.Line("<!DOCTYPE html>");
            using (writer.Scope("<html>"))
            {
                using (writer.Scope("<head>"))
                {
                    writer.Line("<meta charset=\"UTF-8\">");
                    if (UsesAdSupport(project))
                    {
                        writer.Line("<script src=\"mraid.js\"></script>");
                    }

                    if (project.Settings.RunBabel)
                    {
                        // Babelize user code
                        var          title         = $"{UTinyConstants.ApplicationName} Build";
                        const string messageFormat = "Transpiling {0} to ECMAScript 5";

                        EditorUtility.DisplayProgressBar(title, "Transpiling to ECMAScript 5", 0.0f);
                        try
                        {
                            // We only need to transpile user authored code
                            var userCode = new [] { systemsFile, codeFile };
                            var babelDir = new DirectoryInfo(UTinyBuildPipeline.GetToolDirectory("babel"));
                            for (var i = 0; i < userCode.Length; i++)
                            {
                                var file = userCode[i];
                                EditorUtility.DisplayProgressBar(title, string.Format(messageFormat, file.Name), i / (float)userCode.Length);
                                UTinyBuildUtilities.RunNode(babelDir, "index.js", $"\"{file.FullName}\" \"{file.FullName}\"");
                            }
                        }
                        finally
                        {
                            EditorUtility.ClearProgressBar();
                        }
                    }

                    // Gather all game files (order is important)
                    var files = new List <FileInfo>
                    {
                        settingsFile,
                        runtimeFile,
                        bindingsFile,
                        assetsFile,
                        entityGroupsFile,
                        systemsFile,
                        codeFile,
                        mainFile,
                        webSocketClientFile,
                        webpDecompressorFile
                    }.Where(file => file != null && file.Exists).ToList();

                    // Extra steps for Release config
                    if (options.Configuration == UTinyBuildConfiguration.Release)
                    {
                        // Minify JavaScript
                        var gameFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, "game.js"));
                        EditorUtility.DisplayProgressBar($"{UTinyConstants.ApplicationName} Build", "Minifying JavaScript code...", 0.0f);
                        try
                        {
                            var minifyDir = new DirectoryInfo(UTinyBuildPipeline.GetToolDirectory("minify"));
                            UTinyBuildUtilities.RunNode(minifyDir, "index.js", $"\"{gameFile.FullName}\" {String.Join(" ", files.Select(file => '"' + file.FullName + '"'))}");
                            files.ForEach(file => file.Delete());
                        }
                        finally
                        {
                            EditorUtility.ClearProgressBar();
                        }

                        // Package as single html file
                        if (project.Settings.SingleFileHtml)
                        {
                            writer.Line("<script type=\"text/javascript\">");
                            writer.WriteRaw(File.ReadAllText(gameFile.FullName));
                            writer.Line();
                            writer.Line("</script>");
                            gameFile.Delete();
                        }
                        else
                        {
                            writer.LineFormat("<script src=\"{0}\"></script>", gameFile.Name);
                        }
                    }
                    else
                    {
                        files.ForEach(file => writer.LineFormat("<script src=\"{0}\"></script>", file.Name));
                    }
                    writer.LineFormat("<title>{0}</title>", project.Name);
                    writer.CodeStyle.EndBrace = "</head>";
                }
                using (writer.Scope("<body>"))
                {
                    writer.CodeStyle.EndBrace = "</body>";
                }
                writer.CodeStyle.EndBrace = "</html>";
            }

            // Write final index.html file
            var htmlFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KHtmlFileName));

            File.WriteAllText(htmlFile.FullName, writer.ToString(), Encoding.UTF8);
        }
        /// <summary>
        /// Packages system objects to `systems.js`
        ///
        /// All systems and system dependencies are written to this file
        /// </summary>
        private static void GenerateSystems(UTinyBuildOptions options, UTinyBuildResults results)
        {
            var project = options.Project;
            var report  = results.BuildReport.GetOrAddChild(UTinyBuildReport.CodeNode).AddChild();

            var file   = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KSystemsFileName));
            var writer = new UTinyCodeWriter(CodeStyle.JavaScript);

            PrependGeneratedHeader(writer, options.Project.Name);

            foreach (var reference in project.Module.Dereference(project.Registry).GetSystemExecutionOrder())
            {
                var system = reference.Dereference(project.Registry);

                if (system == null)
                {
                    Debug.LogWarning($"Can't resolve system named '{reference.Name}' with ID {reference.Id} -- ignoring, you should delete this system");
                    continue;
                }

                if (system.External)
                {
                    continue;
                }

                // Fetch the module this system belongs to
                var systemModule = UTinyUtility.GetModules(system).FirstOrDefault();

                if (system.IsRuntimeIncluded)
                {
                    continue;
                }

                var reportSystemPos = writer.Length;

                UTinyJsdoc.WriteSystem(writer, system);

                writer.Line($"{UTinyBuildPipeline.GetJsTypeName(systemModule, system)}.update = {UTinyBuildPipeline.GenerateSystemPrefix(system)}");
                writer.IncrementIndent();

                if (system.IncludeIterator)
                {
                    writer.Line(UTinyBuildPipeline.GenerateSystemIteratorPrefix(system));
                    writer.IncrementIndent();
                }

                var text = system.TextAsset ? system.TextAsset.text : string.Empty;

                if (!string.IsNullOrEmpty(text))
                {
                    var lines = text.Split('\n');

                    foreach (var line in lines)
                    {
                        writer.Line(line);
                    }
                }

                if (system.IncludeIterator)
                {
                    writer.DecrementIndent();
                    writer.Line("});");
                }

                writer.DecrementIndent();
                writer.Line(UTinyBuildPipeline.GenerateSystemSuffix(system));

                report.AddChild(AssetDatabase.GetAssetPath(system.TextAsset), Encoding.ASCII.GetBytes(writer.Substring(reportSystemPos)), system.TextAsset);
            }

            File.WriteAllText(file.FullName, writer.ToString(), Encoding.UTF8);
            report.Reset(file);
        }
        /// <summary>
        /// Generates entry point for the applicaton `main.js`
        /// This script will contain the system scheduling, window setup and initial group loading
        /// </summary>
        private static void GenerateMain(UTinyBuildOptions options, UTinyBuildResults results)
        {
            var project  = options.Project;
            var registry = project.Registry;
            var module   = project.Module.Dereference(registry);

            var file = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KMainFileName));

            var writer = new UTinyCodeWriter();

            PrependGeneratedHeader(writer, options.Project.Name);

            var distVersionFile = new FileInfo("UTiny/version.txt");
            var versionString   = "internal";

            if (distVersionFile.Exists)
            {
                versionString = File.ReadAllText(distVersionFile.FullName);
            }
            writer.LineFormat("console.log('runtime version: {0}');", versionString)
            .Line();

            var namespaces = new Dictionary <string, string>();

            foreach (var m in module.EnumerateDependencies())
            {
                if (string.IsNullOrEmpty(m.Namespace))
                {
                    continue;
                }

                if (m.IsRuntimeIncluded)
                {
                    writer.Line($"ut.importModule({m.Namespace});");
                    continue;
                }

                string content;
                namespaces.TryGetValue(m.Namespace, out content);
                content += m.Documentation.Summary;
                namespaces[m.Namespace] = content;
            }

            UTinyJsdoc.WriteType(writer, "ut.World", "Singleton world instance");
            writer.Line("var world;");
            using (writer.Scope("ut.main = function()"))
            {
                // Create and setup the world
                writer
                .Line("world = new ut.World();")
                .Line("var options = WorldSetup(world);");

                // Write configurations
                var context = new EntityGroupSetupVisitor.VisitorContext
                {
                    Project        = project,
                    Module         = project.Module.Dereference(project.Registry),
                    Registry       = project.Registry,
                    Writer         = writer,
                    EntityIndexMap = null
                };

                var configuration = project.Configuration.Dereference(registry);
                foreach (var component in configuration.Components)
                {
                    var moduleContainingType = registry.FindAllByType <UTinyModule>().First(m => m.Types.Contains(component.Type));
                    if (!module.EnumerateDependencies().Contains(moduleContainingType))
                    {
                        // Silently ignore components if the module is not included.
                        // This is by design to preserve user data
                        continue;
                    }

                    var type  = component.Type.Dereference(component.Registry);
                    var index = ++context.ComponentIndex;
                    writer.Line($"var c{index} = world.config({UTinyBuildPipeline.GetJsTypeName(type)});");
                    component.Properties.Visit(new EntityGroupSetupVisitor.ComponentVisitor
                    {
                        VisitorContext = context,
                        Path           = $"c{index}",
                    });
                }

                // Setup the scheduler
                writer.Line("var scheduler = world.scheduler();");

                // Schedule all systems
                var systems = project.Module.Dereference(project.Registry).GetSystemExecutionOrder();
                foreach (var reference in systems)
                {
                    var system = reference.Dereference(project.Registry);

                    if (system == null)
                    {
                        Debug.LogWarning($"Can't resolve system named '{reference.Name}' with ID {reference.Id} -- ignoring, you should delete this system");
                        continue;
                    }

                    var systemModule = UTinyUtility.GetModules(system).FirstOrDefault();
                    var systemName   = UTinyBuildPipeline.GetJsTypeName(systemModule, system);
                    writer.LineFormat("scheduler.schedule({0});", systemName);
                }

                // Enable/disable systems
                foreach (var reference in systems)
                {
                    var system = reference.Dereference(project.Registry);

                    // By default systems are enabled when scheduled, nothing to write
                    if (system == null || system.Enabled)
                    {
                        continue;
                    }

                    var systemModule = UTinyUtility.GetModules(system).FirstOrDefault();
                    var systemName   = UTinyBuildPipeline.GetJsTypeName(systemModule, system);

                    // @NOTE Disable currently accepts a string and NOT the `ut.System` object
                    writer.LineFormat("scheduler.disable({0});", EscapeJsString(systemName));
                }

                writer.Line("try { ut.Runtime.Service.run(world); } catch (e) { if (e !== 'SimulateInfiniteLoop') throw e; }");
            }

            writer.Line();

            using (writer.Scope("function WorldSetup(world)"))
            {
                writer.LineFormat("UT_ASSETS_SETUP(world);");

                var startupEntityGroup = module.StartupEntityGroup.Dereference(module.Registry);

                if (null != startupEntityGroup)
                {
                    writer.Line($"{KEntityGroupNamespace}.{module.Namespace}[\"{module.StartupEntityGroup.Dereference(module.Registry).Name}\"].load(world);");
                }
                else
                {
                    Debug.LogError($"{UTinyConstants.ApplicationName}: BuildError - No startup group has been set");
                }

                using (writer.Scope("return"))
                {
                    writer
                    .LineFormat("canvasWidth: {0},", project.Settings.CanvasWidth)
                    .LineFormat("canvasHeight: {0},", project.Settings.CanvasHeight)
                    .LineFormat("canvasAutoResize: {0},", project.Settings.CanvasAutoResize ? "true" : "false");
                }

#if UNITY_EDITOR_WIN
                writer.Length -= 2;
#else
                writer.Length -= 1;
#endif
                writer.WriteRaw(";").Line();
            }

            File.WriteAllText(file.FullName, writer.ToString(), Encoding.UTF8);
            results.BuildReport.GetOrAddChild(UTinyBuildReport.CodeNode).AddChild(file);
        }