// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using FlaxEditor.Content.Settings; using FlaxEditor.CustomEditors; using FlaxEditor.GUI; using FlaxEditor.GUI.Tabs; using FlaxEditor.Utilities; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; // ReSharper disable InconsistentNaming // ReSharper disable MemberCanBePrivate.Local #pragma warning disable 649 namespace FlaxEditor.Windows { /// /// Editor tool window for building games using . /// /// public sealed class GameCookerWindow : EditorWindow { /// /// Proxy object for the Build tab. /// [CustomEditor(typeof(BuildTabProxy.Editor))] private class BuildTabProxy { public readonly GameCookerWindow GameCookerWin; public readonly PlatformSelector Selector; public readonly Dictionary PerPlatformOptions = new Dictionary { { PlatformType.Windows, new Windows() }, { PlatformType.XboxOne, new XboxOne() }, { PlatformType.UWP, new UWP() }, { PlatformType.Linux, new Linux() }, { PlatformType.PS4, new PS4() }, { PlatformType.XboxScarlett, new XboxScarlett() }, { PlatformType.Android, new Android() }, }; public BuildTabProxy(GameCookerWindow win, PlatformSelector platformSelector) { GameCookerWin = win; Selector = platformSelector; PerPlatformOptions[PlatformType.Windows].Init("Output/Windows", "Windows"); PerPlatformOptions[PlatformType.XboxOne].Init("Output/XboxOne", "XboxOne"); PerPlatformOptions[PlatformType.UWP].Init("Output/UWP", "UWP"); PerPlatformOptions[PlatformType.Linux].Init("Output/Linux", "Linux"); PerPlatformOptions[PlatformType.PS4].Init("Output/PS4", "PS4"); PerPlatformOptions[PlatformType.XboxScarlett].Init("Output/XboxScarlett", "XboxScarlett"); PerPlatformOptions[PlatformType.Android].Init("Output/Android", "Android"); } public abstract class Platform { [HideInEditor] public bool IsAvailable; [EditorOrder(10), Tooltip("Output folder path")] public string Output; [EditorOrder(11), Tooltip("Show output folder in Explorer after build")] public bool ShowOutput = true; [EditorOrder(20), Tooltip("Configuration build mode")] public BuildConfiguration ConfigurationMode = BuildConfiguration.Development; protected abstract BuildPlatform BuildPlatform { get; } protected virtual BuildOptions Options { get { BuildOptions options = BuildOptions.None; if (ShowOutput) options |= BuildOptions.ShowOutput; return options; } } public virtual void Init(string output, string platformDataSubDir) { Output = output; // TODO: restore build settings from the Editor cache! // Check if can find installed tools for this platform IsAvailable = Directory.Exists(Path.Combine(Globals.StartupFolder, "Source", "Platforms", platformDataSubDir, "Binaries")); } public virtual void OnNotAvailableLayout(LayoutElementsContainer layout) { layout.Label("Missing platform data tools for the target platform.", TextAlignment.Center); if (FlaxEditor.Editor.IsOfficialBuild()) { switch (BuildPlatform) { case BuildPlatform.Windows32: case BuildPlatform.Windows64: case BuildPlatform.UWPx86: case BuildPlatform.UWPx64: case BuildPlatform.LinuxX64: case BuildPlatform.AndroidARM64: layout.Label("Use Flax Launcher and download the required package.", TextAlignment.Center); break; default: layout.Label("Engine source is required to target this platform.", TextAlignment.Center); break; } } else { var label = layout.Label("To target this platform separate engine source package is required.\nTo get access please contact via https://flaxengine.com/contact", TextAlignment.Center); label.Label.AutoHeight = true; } } public virtual void Build() { var output = StringUtils.ConvertRelativePathToAbsolute(Globals.ProjectFolder, StringUtils.NormalizePath(Output)); GameCooker.Build(BuildPlatform, ConfigurationMode, output, Options); } } public class Windows : Platform { protected override BuildPlatform BuildPlatform => BuildPlatform.Windows64; } public class UWP : Platform { protected override BuildPlatform BuildPlatform => BuildPlatform.UWPx64; } public class XboxOne : Platform { protected override BuildPlatform BuildPlatform => BuildPlatform.XboxOne; } public class Linux : Platform { protected override BuildPlatform BuildPlatform => BuildPlatform.LinuxX64; } public class PS4 : Platform { protected override BuildPlatform BuildPlatform => BuildPlatform.PS4; } public class XboxScarlett : Platform { protected override BuildPlatform BuildPlatform => BuildPlatform.XboxScarlett; } public class Android : Platform { protected override BuildPlatform BuildPlatform => BuildPlatform.AndroidARM64; } public class Editor : CustomEditor { private PlatformType _platform; private Button _buildButton; public override void Initialize(LayoutElementsContainer layout) { var proxy = (BuildTabProxy)Values[0]; _platform = proxy.Selector.Selected; var platformObj = proxy.PerPlatformOptions[_platform]; if (platformObj.IsAvailable) { string name; switch (_platform) { case PlatformType.Windows: name = "Windows"; break; case PlatformType.XboxOne: name = "Xbox One"; break; case PlatformType.UWP: name = "Windows Store"; break; case PlatformType.Linux: name = "Linux"; break; case PlatformType.PS4: name = "PlayStation 4"; break; case PlatformType.XboxScarlett: name = "Xbox Scarlett"; break; case PlatformType.Android: name = "Android"; break; default: name = CustomEditorsUtil.GetPropertyNameUI(_platform.ToString()); break; } var group = layout.Group(name); group.Object(new ReadOnlyValueContainer(platformObj)); _buildButton = layout.Button("Build").Button; _buildButton.Clicked += OnBuildClicked; } else { platformObj.OnNotAvailableLayout(layout); } } private void OnBuildClicked() { var proxy = (BuildTabProxy)Values[0]; var platformObj = proxy.PerPlatformOptions[_platform]; platformObj.Build(); } public override void Refresh() { base.Refresh(); if (_buildButton != null) { _buildButton.Enabled = !GameCooker.IsRunning; } if (Values.Count > 0 && Values[0] is BuildTabProxy proxy && proxy.Selector.Selected != _platform) { RebuildLayout(); } } } } private class PresetsTargetsColumnBase : ContainerControl { protected GameCookerWindow _cooker; protected PresetsTargetsColumnBase(ContainerControl parent, GameCookerWindow cooker, bool isPresets, Action addClicked) { AnchorPreset = AnchorPresets.VerticalStretchLeft; Parent = parent; Offsets = new Margin(isPresets ? 0 : 140, 140, 0, 0); _cooker = cooker; var title = new Label { Bounds = new Rectangle(0, 0, Width, 19), Text = isPresets ? "Presets" : "Targets", Parent = this, }; var addButton = new Button { Text = isPresets ? "New preset" : "Add target", Bounds = new Rectangle(6, 22, Width - 12, title.Bottom), Parent = this, }; addButton.Clicked += addClicked; } protected void RemoveButtons() { for (int i = ChildrenCount - 1; i >= 0; i--) { if (Children[i].Tag != null) { Children[i].Dispose(); } } } protected void AddButton(string name, int index, int selectedIndex, Action