diff --git a/Editor/Build.meta b/Editor/Build.meta new file mode 100644 index 0000000..2378bc8 --- /dev/null +++ b/Editor/Build.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4d9f862e28154a8686fbce5a3ace4f9d +timeCreated: 1744231598 \ No newline at end of file diff --git a/Editor/Build/BuildVersionBumper.cs b/Editor/Build/BuildVersionBumper.cs new file mode 100644 index 0000000..a827a3d --- /dev/null +++ b/Editor/Build/BuildVersionBumper.cs @@ -0,0 +1,72 @@ +using UnityEngine; +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; + +namespace RebootKitEditor.Build { + class BuildVersionBumper : IPostprocessBuildWithReport { + public int callbackOrder => 0; + + public void OnPostprocessBuild(BuildReport report) { + VersionUpdater.IncrementPatch(); + } + } + + static class VersionUpdater { + public static bool IncrementVersion(int incrementMajor = 0, int incrementMinor = 0, int incrementPatch = 0) { + if (TryParseVersion(PlayerSettings.bundleVersion, out int major, out int minor, out int patch)) { + major += incrementMajor; + minor += incrementMinor; + patch += incrementPatch; + string newVersion = $"{major}.{minor}.{patch}"; + + if (SetVersion(newVersion)) { + Debug.Log($"Updated version to: {newVersion}"); + } else { + Debug.LogError($"Failed to update version to: {newVersion}"); + return false; + } + } else { + Debug.LogError($"Failed to parse current version: {PlayerSettings.bundleVersion}"); + return false; + } + + return true; + } + + public static bool IncrementMajor() => IncrementVersion(incrementMajor: 1); + public static bool IncrementMinor() => IncrementVersion(incrementMinor: 1); + public static bool IncrementPatch() => IncrementVersion(incrementPatch: 1); + + static bool SetVersion(string newVersion) { + if (!IsValidVersionFormat(newVersion)) { + Debug.LogError($"Invalid version format: {newVersion}. Expected format: maj.min.ver"); + return false; + } + + PlayerSettings.bundleVersion = newVersion; + Debug.Log($"Application version updated to: {newVersion}"); + return true; + } + + static bool IsValidVersionFormat(string version) { + string[] parts = version.Split('.'); + return parts.Length == 3 && int.TryParse(parts[0], out _) && int.TryParse(parts[1], out _) && int.TryParse(parts[2], out _); + } + + static bool TryParseVersion(string version, out int major, out int minor, out int patch) { + major = 0; + minor = 0; + patch = 0; + + string[] parts = version.Split('.'); + if (parts.Length != 3) { + return false; + } + + return int.TryParse(parts[0], out major) && + int.TryParse(parts[1], out minor) && + int.TryParse(parts[2], out patch); + } + } +} \ No newline at end of file diff --git a/Editor/Build/BuildVersionBumper.cs.meta b/Editor/Build/BuildVersionBumper.cs.meta new file mode 100644 index 0000000..c7b4b98 --- /dev/null +++ b/Editor/Build/BuildVersionBumper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 75ae1e3647224db7beaab84c858cb20b +timeCreated: 1744231606 \ No newline at end of file diff --git a/Editor/CommonEditorActions.cs b/Editor/CommonEditorActions.cs new file mode 100644 index 0000000..6658d3b --- /dev/null +++ b/Editor/CommonEditorActions.cs @@ -0,0 +1,38 @@ +using System.IO; +using RebootKitEditor.Build; +using UnityEditor; +using UnityEngine; + +namespace RebootKitEditor { + static class CommonEditorActions { + [MenuItem(REditorConsts.k_EditorMenu + "Bump minor version", false, 0)] + static void BumpMinorVersion() { + if (VersionUpdater.IncrementMinor()) { + Debug.Log("Bumped minor version."); + } else { + Debug.LogError("Failed to bump minor version."); + } + } + + [MenuItem(REditorConsts.k_EditorMenu + "Open Persistent Data Folder", false, 100)] + static void OpenPersistentDataFolder() { + string path = Application.persistentDataPath; + if (Directory.Exists(path)) { + EditorUtility.RevealInFinder(path); + } else { + Debug.LogError($"Persistent data folder does not exist: {path}"); + } + } + + [MenuItem(REditorConsts.k_EditorMenu + "Remove Persistent Data Folder", false, 1000)] + static void RemovePersistentDataFolder() { + string path = Application.persistentDataPath; + if (Directory.Exists(path)) { + Directory.Delete(path, true); + Debug.Log($"Removed persistent data folder: {path}"); + } else { + Debug.LogError($"Persistent data folder does not exist: {path}"); + } + } + } +} \ No newline at end of file diff --git a/Editor/CommonEditorActions.cs.meta b/Editor/CommonEditorActions.cs.meta new file mode 100644 index 0000000..506d3f3 --- /dev/null +++ b/Editor/CommonEditorActions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 81870cc212f846b0a15b9b4334aa5261 +timeCreated: 1744230412 \ No newline at end of file diff --git a/Editor/Inspectors.meta b/Editor/Inspectors.meta deleted file mode 100644 index bc1a1b1..0000000 --- a/Editor/Inspectors.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: ea037ce7419341d8a7961be6343fd957 -timeCreated: 1741641033 \ No newline at end of file diff --git a/Editor/PropertyDrawers/CVarDrawer.cs b/Editor/PropertyDrawers/CVarDrawer.cs index 7db022b..b8b66ee 100644 --- a/Editor/PropertyDrawers/CVarDrawer.cs +++ b/Editor/PropertyDrawers/CVarDrawer.cs @@ -4,7 +4,7 @@ using UnityEditor; using UnityEngine; namespace RebootKitEditor.PropertyDrawers { - [CustomPropertyDrawer(typeof(CVar))] + [CustomPropertyDrawer(typeof(ConfigVar))] public class CVarDrawer : PropertyDrawer { bool m_Expand; diff --git a/Editor/PropertyDrawers/ConstsPropertyDrawer.cs b/Editor/PropertyDrawers/ConstsPropertyDrawer.cs new file mode 100644 index 0000000..2de0a8c --- /dev/null +++ b/Editor/PropertyDrawers/ConstsPropertyDrawer.cs @@ -0,0 +1,38 @@ +using RebootKit.Engine.Foundation; +using UnityEditor; +using UnityEngine; + +namespace RebootKitEditor.PropertyDrawers { + [CustomPropertyDrawer(typeof(ConstsProperty<>))] + public class ConstsPropertyDrawer : PropertyDrawer { + const string k_InlineValue = "m_InlineValue"; + const string k_Asset = "m_Asset"; + const string k_UseInlineValue = "m_UseInlineValue"; + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + SerializedProperty useInlineValue = property.FindPropertyRelative(k_UseInlineValue); + SerializedProperty inlineValue = property.FindPropertyRelative(k_InlineValue); + SerializedProperty asset = property.FindPropertyRelative(k_Asset); + + EditorGUI.BeginProperty(position, label, property); + + GUILayout.BeginHorizontal(); + + EditorGUILayout.LabelField(label, EditorStyles.miniBoldLabel); + + if (useInlineValue.boolValue) { + EditorGUILayout.PropertyField(inlineValue, GUIContent.none); + } else { + EditorGUILayout.PropertyField(asset, GUIContent.none); + } + + if (GUILayout.Button(useInlineValue.boolValue ? "Inline" : "Asset", GUILayout.MaxWidth(100))) { + useInlineValue.boolValue = !useInlineValue.boolValue; + } + + GUILayout.EndHorizontal(); + + EditorGUI.EndProperty(); + } + } +} \ No newline at end of file diff --git a/Editor/PropertyDrawers/ConstsPropertyDrawer.cs.meta b/Editor/PropertyDrawers/ConstsPropertyDrawer.cs.meta new file mode 100644 index 0000000..8de1ab8 --- /dev/null +++ b/Editor/PropertyDrawers/ConstsPropertyDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d7ed7ab023f349a4a4b50ee7a4029178 +timeCreated: 1743848631 \ No newline at end of file diff --git a/Editor/REditorConsts.cs b/Editor/REditorConsts.cs new file mode 100644 index 0000000..d96f695 --- /dev/null +++ b/Editor/REditorConsts.cs @@ -0,0 +1,5 @@ +namespace RebootKitEditor { + static class REditorConsts { + internal const string k_EditorMenu = "Reboot Reality/"; + } +} \ No newline at end of file diff --git a/Editor/REditorConsts.cs.meta b/Editor/REditorConsts.cs.meta new file mode 100644 index 0000000..7fd0158 --- /dev/null +++ b/Editor/REditorConsts.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c12d260ac43a4a95a0cb5bf7e3260886 +timeCreated: 1744230534 \ No newline at end of file diff --git a/Editor/RebootKitEditor.asmdef b/Editor/RebootKitEditor.asmdef index 7bec58d..df38dfc 100644 --- a/Editor/RebootKitEditor.asmdef +++ b/Editor/RebootKitEditor.asmdef @@ -1,18 +1,20 @@ { - "name": "SzafaKitEditor", - "rootNamespace": "RebootKitEditor", - "references": [ - "GUID:284059c7949783646b281a1b815580e6" - ], - "includePlatforms": [ - "Editor" - ], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": true, - "defineConstraints": [], - "versionDefines": [], - "noEngineReferences": false + "name": "SzafaKitEditor", + "rootNamespace": "RebootKitEditor", + "references": [ + "GUID:284059c7949783646b281a1b815580e6", + "GUID:9e24947de15b9834991c9d8411ea37cf", + "GUID:69448af7b92c7f342b298e06a37122aa" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/Editor/RebootWindow.meta b/Editor/RebootWindow.meta new file mode 100644 index 0000000..fb65000 --- /dev/null +++ b/Editor/RebootWindow.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: af51a267b80341a7a06264976e356e49 +timeCreated: 1744413473 \ No newline at end of file diff --git a/Editor/RebootWindow/ConfigVarsView.cs b/Editor/RebootWindow/ConfigVarsView.cs new file mode 100644 index 0000000..5c71b55 --- /dev/null +++ b/Editor/RebootWindow/ConfigVarsView.cs @@ -0,0 +1,105 @@ +using RebootKit.Engine.Foundation; +using RebootKit.Engine.Services.Console; +using RebootKit.Engine.UI; +using UnityEngine; +using UnityEngine.UIElements; + +namespace RebootKitEditor.RebootWindow { + public class ConfigVarsView : IView { + public void Dispose() { + } + + public VisualElement Build() { + ScrollView scrollView = new() { + style = { + flexGrow = 1 + } + }; + + ConfigVarsContainer.Init(); + + foreach (ConfigVar cvar in ConfigVarsContainer.All()) { + VisualElement varContainer = new() { + style = { + marginBottom = 8, + paddingBottom = 8, + borderBottomWidth = 1, + borderBottomColor = new Color(0.3f, 0.3f, 0.3f), + } + }; + + VisualElement valueContainer = new() { + style = { + flexDirection = FlexDirection.Row + } + }; + varContainer.Add(valueContainer); + + Label nameLabel = new(cvar.name) { + style = { + color = new Color(0.7f, 0.9f, 0.9f), + unityFontStyleAndWeight = FontStyle.Bold + } + }; + valueContainer.Add(nameLabel); + + Label valueLabel = new(cvar.ToString()) { + style = { + color = RTheme.s_FirstColor + } + }; + valueContainer.Add(valueLabel); + + if (cvar.flags.HasFlag(CVarFlags.User)) { + valueContainer.Add(CreateFlagLabel("User", new Color(0.36f, 0.41f, 0.42f))); + } + + if (cvar.flags.HasFlag(CVarFlags.Client)) { + valueContainer.Add(CreateFlagLabel("Client", new Color(0.81f, 0.29f, 0.15f))); + } + + if (cvar.flags.HasFlag(CVarFlags.Server)) { + valueContainer.Add(CreateFlagLabel("Server", new Color(0.18f, 0.64f, 0.18f))); + } + + if (cvar.flags.HasFlag(CVarFlags.ReadOnly)) { + valueContainer.Add(CreateFlagLabel("ReadOnly", new Color(0.13f, 0.07f, 0.47f))); + } + + Label descLabel = new(cvar.description) { + style = { + fontSize = 10, + color = new Color(0.7f, 0.7f, 0.7f) + } + }; + varContainer.Add(descLabel); + + scrollView.Add(varContainer); + } + + return scrollView; + } + + VisualElement CreateFlagLabel(string text, Color color) { + Label label = new(text) { + style = { + fontSize = 12, + color = new Color(0.7f, 0.7f, 0.7f), + backgroundColor = color, + paddingLeft = 4, + paddingRight = 4, + paddingTop = 4, + paddingBottom = 4, + marginLeft = 4, + marginRight = 4, + borderTopLeftRadius = 8, + borderTopRightRadius = 8, + borderBottomLeftRadius = 8, + borderBottomRightRadius = 8, + unityFontStyleAndWeight = FontStyle.Bold, + } + }; + return label; + } + } +} \ No newline at end of file diff --git a/Editor/RebootWindow/ConfigVarsView.cs.meta b/Editor/RebootWindow/ConfigVarsView.cs.meta new file mode 100644 index 0000000..c45e258 --- /dev/null +++ b/Editor/RebootWindow/ConfigVarsView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7572c4418faf4e74930030a016e3dfc6 +timeCreated: 1744461313 \ No newline at end of file diff --git a/Editor/RebootWindow/GameServicesView.cs b/Editor/RebootWindow/GameServicesView.cs new file mode 100644 index 0000000..b67f6f6 --- /dev/null +++ b/Editor/RebootWindow/GameServicesView.cs @@ -0,0 +1,103 @@ +using System.Collections.Generic; +using RebootKit.Engine; +using RebootKit.Engine.Foundation; +using RebootKit.Engine.UI; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.UIElements; + +namespace RebootKitEditor.RebootWindow { + public class GameServicesView : IView { + VisualElement m_RootElement; + + readonly List m_ServiceEditors = new(); + + [Inject] EngineConfigAsset m_EngineConfigAsset; + + public void Dispose() { + foreach (Editor editor in m_ServiceEditors) { + if (editor != null) { + Object.DestroyImmediate(editor); + } + } + m_ServiceEditors.Clear(); + } + + public VisualElement Build() { + m_RootElement = new ScrollView(); + + Label servicesAmountLabel = new($"Game services: {m_EngineConfigAsset.services.Length}") { + style = { + color = new Color(0.7f, 0.9f, 0.9f), + unityFontStyleAndWeight = FontStyle.Bold + } + }; + m_RootElement.Add(servicesAmountLabel); + + for (int i = 0; i < m_EngineConfigAsset.services.Length; i++) { + ServiceAsset serviceAsset = m_EngineConfigAsset.services[i]; + + VisualElement serviceView = CreateServicesView(serviceAsset); + serviceView.style.backgroundColor = i % 2 == 0 ? new Color(0.1f, 0.1f, 0.1f) : new Color(0.2f, 0.2f, 0.2f); + m_RootElement.Add(serviceView); + } + + return m_RootElement; + } + + VisualElement CreateServicesView(ServiceAsset serviceAsset) where T : class, IService { + VisualElement root = new() { + style = { + paddingBottom = 4, + paddingTop = 4, + paddingLeft = 4, + paddingRight = 4, + borderBottomLeftRadius = 4, + borderBottomRightRadius = 4 + } + }; + + VisualElement header = new() { + style = { + backgroundColor = new Color(0.2f, 0.2f, 0.2f), + paddingLeft = 8, + paddingRight = 8, + paddingTop = 4, + paddingBottom = 4, + borderTopLeftRadius = 4, + borderTopRightRadius = 4, + } + }; + root.Add(header); + + Label nameLabel = new(serviceAsset.name) { + style = { + color = new Color(0.7f, 0.9f, 0.9f), + unityFontStyleAndWeight = FontStyle.Bold + } + }; + header.Add(nameLabel); + + VisualElement editorView = new() { + style = { + backgroundColor = new Color(0.3f, 0.3f, 0.3f), + paddingLeft = 10, + paddingRight = 10, + paddingTop = 5, + paddingBottom = 5, + minHeight = 50 + } + }; + root.Add(editorView); + + Editor editor = Editor.CreateEditor(serviceAsset); + m_ServiceEditors.Add(editor); + + InspectorElement inspectorElement = new(editor); + editorView.Add(inspectorElement); + + return root; + } + } +} \ No newline at end of file diff --git a/Editor/RebootWindow/GameServicesView.cs.meta b/Editor/RebootWindow/GameServicesView.cs.meta new file mode 100644 index 0000000..bda0b01 --- /dev/null +++ b/Editor/RebootWindow/GameServicesView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 08e99ca3a4a641069f1f7a1b30388754 +timeCreated: 1744552856 \ No newline at end of file diff --git a/Editor/RebootWindow/HomeView.cs b/Editor/RebootWindow/HomeView.cs new file mode 100644 index 0000000..5138954 --- /dev/null +++ b/Editor/RebootWindow/HomeView.cs @@ -0,0 +1,62 @@ +using RebootKit.Engine.UI; +using UnityEngine; +using UnityEngine.UIElements; + +namespace RebootKitEditor.RebootWindow { + public class HomeView : IView { + public void Dispose() { + } + + public VisualElement Build() { + VisualElement rootContainer = new() { + style = { + flexGrow = 1, + fontSize = 14 + } + }; + + Label label = new($"{Application.productName} {Application.version}") { + style = { + fontSize = 18, + unityFontStyleAndWeight = FontStyle.Bold + } + }; + rootContainer.Add(label); + + VisualElement persistentPathContainer = new() { + style = { + marginTop = 8, + marginBottom = 8, + paddingLeft = 4, + paddingRight = 4, + paddingTop = 4, + paddingBottom = 4, + borderLeftWidth = 1, + borderLeftColor = new Color(0.3f, 0.3f, 0.3f), + flexDirection = FlexDirection.Row, + } + }; + + Label persistentPathLabel = new($"Persistent Path: {Application.persistentDataPath}") { + style = { + fontSize = 12, + color = new Color(0.7f, 0.9f, 0.9f) + } + }; + persistentPathContainer.Add(persistentPathLabel); + + Button openPersistentPathButton = new(() => { Application.OpenURL(Application.persistentDataPath); }) { + style = { + fontSize = 12, + width = 48 + }, + text = "Open" + }; + persistentPathContainer.Add(openPersistentPathButton); + + rootContainer.Add(persistentPathContainer); + + return rootContainer; + } + } +} \ No newline at end of file diff --git a/Editor/RebootWindow/HomeView.cs.meta b/Editor/RebootWindow/HomeView.cs.meta new file mode 100644 index 0000000..d4a245d --- /dev/null +++ b/Editor/RebootWindow/HomeView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1e5060e00fe74a0aa4013a814a91137c +timeCreated: 1744462818 \ No newline at end of file diff --git a/Editor/RebootWindow/RebootEditorWindow.cs b/Editor/RebootWindow/RebootEditorWindow.cs new file mode 100644 index 0000000..3d95c60 --- /dev/null +++ b/Editor/RebootWindow/RebootEditorWindow.cs @@ -0,0 +1,76 @@ +using RebootKit.Engine; +using RebootKit.Engine.Foundation; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; +using Logger = RebootKit.Engine.Foundation.Logger; + +namespace RebootKitEditor.RebootWindow { + static class RTheme { + public static readonly Color s_FirstColor = ColorFromHex("#B9B8B9"); + public static readonly Color s_SecondColor = ColorFromHex("#6B6B6B"); + public static readonly Color s_BackgroundPrimaryColor = EditorGUIUtility.isProSkin ? ColorFromHex("#1E1E1E") : ColorFromHex("#F0F0F0"); + public static readonly Color s_BackgroundSecondaryColor = ColorFromHex("#242126"); + public static readonly Color s_TextColor = ColorFromHex("#696969"); + + static Color ColorFromHex(string hex) { + if (ColorUtility.TryParseHtmlString(hex, out Color color)) { + return color; + } else { + Debug.LogError($"Failed to parse color from hex: {hex}"); + return Color.white; + } + } + } + + public class RebootEditorWindow : EditorWindow { + static readonly Logger s_logger = new(nameof(RebootEditorWindow)); + + EngineConfigAsset m_EngineConfigAsset; + DIContext m_DIContext; + + VisualElement m_RootElement; + TabView m_TabView; + + [MenuItem(REditorConsts.k_EditorMenu + "RebootKit")] + public static void ShowWindow() { + RebootEditorWindow window = GetWindow($"RebootKit - {Application.productName}"); + window.Show(); + } + + void OnEnable() { + m_EngineConfigAsset = Resources.Load(RConsts.k_EngineConfigResourcesPath); + if (m_EngineConfigAsset == null) { + EngineConfigAsset newConfig = CreateInstance(); + newConfig.name = RConsts.k_EngineConfigAssetName; + AssetDatabase.CreateAsset(newConfig, RConsts.k_EngineConfigAssetPath); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + s_logger.Info($"Created new engine config asset at: {RConsts.k_EngineConfigAssetPath}"); + + m_EngineConfigAsset = Resources.Load(RConsts.k_EngineConfigResourcesPath); + if (m_EngineConfigAsset == null) { + s_logger.Error($"Couldn't load engine config from resources: {RConsts.k_EngineConfigResourcesPath}"); + return; + } + } + + m_DIContext = new DIContext(); + m_DIContext.Bind(this); + m_DIContext.Bind(m_EngineConfigAsset); + + m_TabView = m_DIContext.Create(); + m_TabView.AddTab("Home", m_DIContext.Create()); + m_TabView.AddTab("Config Vars", m_DIContext.Create()); + m_TabView.AddTab("Game Services", m_DIContext.Create()); + m_TabView.AddTab("Worlds", m_DIContext.Create()); + } + + void CreateGUI() { + m_RootElement = rootVisualElement; + m_RootElement.Clear(); + + m_RootElement.Add(m_TabView.Build()); + } + } +} \ No newline at end of file diff --git a/Editor/RebootWindow/RebootEditorWindow.cs.meta b/Editor/RebootWindow/RebootEditorWindow.cs.meta new file mode 100644 index 0000000..91dcbe6 --- /dev/null +++ b/Editor/RebootWindow/RebootEditorWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 25dcbb2930b3453cacf147ab342a7bc6 +timeCreated: 1744413484 \ No newline at end of file diff --git a/Editor/RebootWindow/TabView.cs b/Editor/RebootWindow/TabView.cs new file mode 100644 index 0000000..6f22875 --- /dev/null +++ b/Editor/RebootWindow/TabView.cs @@ -0,0 +1,127 @@ +using System.Collections.Generic; +using RebootKit.Engine.UI; +using UnityEngine; +using UnityEngine.Assertions; +using UnityEngine.UIElements; + +namespace RebootKitEditor.RebootWindow { + public class TabView : IView { + struct Tab { + public string name; + public IView view; + public VisualElement container; + } + + readonly List m_Tabs = new(); + readonly List m_TabContents = new(); + readonly List