├── .gitattributes
├── Documentation
├── GameframeFace.gif
└── GameframeFace.gif.meta
├── LICENSE.md.meta
├── README.md.meta
├── package.json.meta
├── Editor.meta
├── Template.meta
├── Template
├── Licenses
│ ├── MIT LICENSE.meta
│ └── MIT LICENSE
├── README_TEMPLATE.md.meta
├── Licenses.meta
└── README_TEMPLATE.md
├── Tests.meta
├── Documentation.meta
├── Editor
├── Utility.meta
├── PackageNpmPublisher.meta
├── com.gameframe.packages.editor.asmdef.meta
├── PackageMenu.cs.meta
├── PackageUtility.cs.meta
├── ShellUtility.cs.meta
├── SimpleJson.cs.meta
├── PackageGuiUtility.cs.meta
├── PackageManifest.cs.meta
├── PackageSettings.cs.meta
├── SourcePackageInfo.cs.meta
├── PackageMaintainerWindow.cs.meta
├── Utility
│ ├── AsyncOperationAwaiter.cs.meta
│ ├── RequestExtensions.cs.meta
│ ├── RequestExtensions.cs
│ └── AsyncOperationAwaiter.cs
├── PackageNpmPublisher
│ ├── PackageNpmPublisher.cs.meta
│ ├── PackageNpmPublisher.uss.meta
│ ├── PackageNpmPublisher.uss
│ ├── PackageNpmPublisher.uxml.meta
│ ├── PackageNpmPublisher.uxml
│ └── PackageNpmPublisher.cs
├── com.gameframe.packages.editor.asmdef
├── PackageSettings.cs
├── PackageManifest.cs
├── PackageGuiUtility.cs
├── SourcePackageInfo.cs
├── PackageMenu.cs
├── PackageUtility.cs
├── ShellUtility.cs
├── PackageMaintainerWindow.cs
└── SimpleJson.cs
├── Tests
├── com.gameframe.packages.tests.asmdef.meta
├── ShellUtilityTests.cs.meta
├── com.gameframe.packages.tests.asmdef
└── ShellUtilityTests.cs
├── package.json
├── LICENSE.md
├── .gitignore
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/Documentation/GameframeFace.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coryleach/UnityPackages/HEAD/Documentation/GameframeFace.gif
--------------------------------------------------------------------------------
/LICENSE.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5c0692abccabdd14ca1bb7fe801b4593
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 971d2fdde302f7341a6c865eed9d198b
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/package.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f93fa2b2db0bfa64189288df110373c6
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 75b3a24d1fb821346963c52b201659d1
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Template.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 154b28759d40efb44bca8538c157d9a8
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Template/Licenses/MIT LICENSE.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1470dfe4373a4994db2f39a13610b4d0
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Template/README_TEMPLATE.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5d9523bf82d25a84c953bfb49219f0d3
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Tests.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 860ad12e0060de94d8e59774913ae548
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Documentation.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: dda4c03853fec46bebff0e3d416ad74b
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/Utility.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: cd8b9a4fdf7cee946864833b9e91bdc8
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Template/Licenses.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a4dec6f1706f123488eaba8ed9e00415
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/PackageNpmPublisher.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7ed3c59643e58ba48af6dd9a0b7079b5
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/com.gameframe.packages.editor.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8774ef0c0d363ae48b23b3dd950d3560
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Tests/com.gameframe.packages.tests.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8c4ce575f3f7d4c59b79cfcce5d62cd9
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Editor/PackageMenu.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4b393b3ddb3a6f3409a8cb3e5a1f98d9
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/PackageUtility.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 46a0693646c231c4dafc66fb56a82fa7
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/ShellUtility.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 595c963e009227f479fc09c643385cdc
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/SimpleJson.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7daa7c68c94dea84abfd91a2151dd115
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/PackageGuiUtility.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3edb703b81145154489cdbda90c4fee0
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/PackageManifest.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 98e17d44e578e0243926fef6ba272cd1
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/PackageSettings.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7fedcd0de29839e4ba6cb907d2f7ca0e
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/SourcePackageInfo.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 80b3d24ddf142d243bd02d26a49d9284
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Tests/ShellUtilityTests.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b1ce4de404aa4bd4b9fff05b54f35cfa
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/PackageMaintainerWindow.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9e15605124527e942b4a6286dbc4e1de
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Utility/AsyncOperationAwaiter.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: abe3b007b34d81545b88b46c62b57305
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Utility/RequestExtensions.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f48bb13575db7b540a49a8c2bac83aa1
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/PackageNpmPublisher/PackageNpmPublisher.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 166f81a40c9f16a49a9713d5d720f2c4
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Utility/RequestExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Gameframe.Packages.Utility
2 | {
3 | public static class RequestExtensions
4 | {
5 | public static AsyncOperationAwaiter GetAwaiter(this UnityEditor.PackageManager.Requests.Request request)
6 | {
7 | return new AsyncOperationAwaiter(()=>request.IsCompleted);
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/Editor/PackageNpmPublisher/PackageNpmPublisher.uss.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7d91d9be09a558f4aa7ee17e557dbd5f
3 | ScriptedImporter:
4 | fileIDToRecycleName:
5 | 11400000: stylesheet
6 | externalObjects: {}
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 | script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
11 |
--------------------------------------------------------------------------------
/Editor/PackageNpmPublisher/PackageNpmPublisher.uss:
--------------------------------------------------------------------------------
1 | .BorderedContainer
2 | {
3 | margin:5px;
4 | border-width:1px;
5 | border-color: rgba(0,0,0,0.5);
6 | background-color: rgba(0,0,0,0.2);
7 | border-radius: 5px;
8 | padding:5px;
9 | }
10 |
11 | .rowEven
12 | {
13 | background-color: rgba(0,0,0,0.20)
14 | }
15 |
16 | .rowOdd
17 | {
18 | background-color: rgba(0,0,0,0.30)
19 | }
20 |
--------------------------------------------------------------------------------
/Editor/PackageNpmPublisher/PackageNpmPublisher.uxml.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c79b1e62247e9b04d91e74f8cc1b97cc
3 | ScriptedImporter:
4 | fileIDToRecycleName:
5 | 11400000: tree
6 | 11400002: inlineStyle
7 | externalObjects: {}
8 | userData:
9 | assetBundleName:
10 | assetBundleVariant:
11 | script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
12 |
--------------------------------------------------------------------------------
/Editor/com.gameframe.packages.editor.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.gameframe.packages.editor",
3 | "references": [
4 | "com.gameframe.packages.runtime"
5 | ],
6 | "optionalUnityReferences": [],
7 | "includePlatforms": [
8 | "Editor"
9 | ],
10 | "excludePlatforms": [],
11 | "allowUnsafeCode": false,
12 | "overrideReferences": false,
13 | "precompiledReferences": [],
14 | "autoReferenced": true,
15 | "defineConstraints": []
16 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.gameframe.packages",
3 | "version": "1.0.2",
4 | "displayName": "Gameframe.Packages",
5 | "description": "Package for creating Unity packages just like this one!",
6 | "repositoryName": "UnityPackages",
7 | "author": {
8 | "name": "Cory Leach",
9 | "email": "cory.leach@gmail.com",
10 | "url": "https://github.com/coryleach",
11 | "github": "coryleach",
12 | "twitter": "coryleach"
13 | },
14 | "unity": "2019.3",
15 | "type": "tool"
16 | }
--------------------------------------------------------------------------------
/Tests/com.gameframe.packages.tests.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.gameframe.packages.tests",
3 | "references": [
4 | "GUID:8774ef0c0d363ae48b23b3dd950d3560"
5 | ],
6 | "optionalUnityReferences": [
7 | "TestAssemblies"
8 | ],
9 | "includePlatforms": [],
10 | "excludePlatforms": [],
11 | "allowUnsafeCode": false,
12 | "overrideReferences": false,
13 | "precompiledReferences": [],
14 | "autoReferenced": true,
15 | "defineConstraints": [],
16 | "versionDefines": []
17 | }
--------------------------------------------------------------------------------
/Editor/PackageNpmPublisher/PackageNpmPublisher.uxml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Editor/PackageSettings.cs:
--------------------------------------------------------------------------------
1 | using UnityEditor;
2 |
3 | namespace Gameframe.Packages.Editor
4 | {
5 | public static class PackageSettings
6 | {
7 | public static string SourcePath
8 | {
9 | get => EditorPrefs.GetString("PackageSourcePath");
10 | set => EditorPrefs.SetString("PackageSourcePath", value);
11 | }
12 |
13 | private static PackageManifest manifest = new PackageManifest();
14 | public static PackageManifest Manifest
15 | {
16 | get => manifest ?? (manifest = new PackageManifest());
17 | set => manifest = value;
18 | }
19 |
20 | public static void Load()
21 | {
22 |
23 | }
24 |
25 | public static void Save()
26 | {
27 |
28 | }
29 |
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Editor/PackageManifest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Gameframe.Packages
4 | {
5 | [Serializable]
6 | public class PackageManifest
7 | {
8 | [Serializable]
9 | public class PackageAuthor
10 | {
11 | public string name = "Cory Leach";
12 | public string email = "cory.leach@gmail.com";
13 | public string url = "https://github.com/coryleach";
14 | public string twitter = "coryleach";
15 | public string github = "coryleach";
16 | }
17 |
18 |
19 | public string githubUrl = "";
20 | public string name = "com.gameframe.mypackagename";
21 | public string displayName = "My Package Name";
22 | public string repositoryName = "RepositoryName";
23 | public string version = "1.0.0";
24 | public string description = "";
25 | public string type = "library"; //tool, module, tests, sample, template, library
26 | public string unity = "";
27 | public string unityRelease = "";
28 | public string[] keywords = new string[0];
29 | public PackageAuthor author = new PackageAuthor();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Editor/Utility/AsyncOperationAwaiter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using System.Threading.Tasks;
4 | using UnityEngine;
5 |
6 | namespace Gameframe.Packages.Utility
7 | {
8 | public class AsyncOperationAwaiter : INotifyCompletion
9 | {
10 | private readonly Func _asyncOperation;
11 | private Action _continuation;
12 |
13 | public AsyncOperationAwaiter(Func asyncOperation)
14 | {
15 | _asyncOperation = asyncOperation;
16 | _continuation = null;
17 | Await();
18 | }
19 |
20 | public void GetResult()
21 | {
22 | }
23 |
24 | public AsyncOperationAwaiter GetAwaiter() => this;
25 |
26 | public bool IsCompleted => _asyncOperation.Invoke();
27 |
28 | public void OnCompleted(Action continuation)
29 | {
30 | _continuation = continuation;
31 | }
32 |
33 | private async void Await()
34 | {
35 | while (!IsCompleted)
36 | {
37 | await Task.Yield();
38 | }
39 | _continuation?.Invoke();
40 | }
41 |
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 cory.leach@gmail.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Editor/PackageGuiUtility.cs:
--------------------------------------------------------------------------------
1 | using UnityEditor;
2 | using UnityEngine;
3 |
4 | namespace Gameframe.Packages.Editor
5 | {
6 | public static class PackageGuiUtility
7 | {
8 | ///
9 | /// Draws the UI for and updates packages source path
10 | ///
11 | /// true if the source path location was modified. Otherwise false.
12 | public static bool SourcePathGui()
13 | {
14 | EditorGUILayout.BeginVertical("box");
15 | EditorGUILayout.LabelField("Source Path");
16 | EditorGUILayout.BeginHorizontal("box");
17 | EditorGUILayout.LabelField(PackageSettings.SourcePath);
18 | if (GUILayout.Button("Browse"))
19 | {
20 | PackageSettings.SourcePath = EditorUtility.OpenFolderPanel("Package Source", PackageSettings.SourcePath, "GitHub");
21 | return true;
22 | }
23 | if (GUILayout.Button("Open"))
24 | {
25 | EditorUtility.RevealInFinder(PackageSettings.SourcePath);
26 | return true;
27 | }
28 | EditorGUILayout.EndHorizontal();
29 | EditorGUILayout.EndVertical();
30 | return false;
31 | }
32 |
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/Editor/SourcePackageInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using UnityEngine;
4 |
5 | namespace Gameframe.Packages
6 | {
7 | public class SourcePackageInfo
8 | {
9 | public SourcePackageInfo(string path)
10 | {
11 | directoryInfo = new DirectoryInfo(path);
12 | try
13 | {
14 | status = Status.NotChecked;
15 | var packageJson = File.ReadAllText($"{path}/package.json");
16 | packageInfo = JsonUtility.FromJson(packageJson);
17 | if (packageInfo == null)
18 | {
19 | status = Status.Error;
20 | error = "Failed to get manifest from json";
21 | }
22 | }
23 | catch (Exception e)
24 | {
25 | status = Status.Error;
26 | error = e.Message;
27 | }
28 | }
29 |
30 | public DirectoryInfo directoryInfo;
31 |
32 | public PackageManifest packageInfo;
33 |
34 | public Status status = Status.NotChecked;
35 |
36 | public string error = string.Empty;
37 |
38 | public enum Status
39 | {
40 | NotChecked,
41 | Error,
42 | NotEmbeded,
43 | Embeded
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Template/Licenses/MIT LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) {DATE.YEAR} {AUTHOR.NAME}
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This .gitignore file should be placed at the root of your Unity project directory
2 | #
3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
4 | #
5 | /[Ll]ibrary/
6 | /[Tt]emp/
7 | /[Oo]bj/
8 | /[Bb]uild/
9 | /[Bb]uilds/
10 | /[Ll]ogs/
11 | /[Mm]emoryCaptures/
12 |
13 | # Never ignore Asset meta data
14 | !/[Aa]ssets/**/*.meta
15 |
16 | # Uncomment this line if you wish to ignore the asset store tools plugin
17 | # /[Aa]ssets/AssetStoreTools*
18 |
19 | # TextMesh Pro files
20 | [Aa]ssets/TextMesh*Pro/
21 |
22 | # Autogenerated Jetbrains Rider plugin
23 | [Aa]ssets/Plugins/Editor/JetBrains*
24 |
25 | # Visual Studio cache directory
26 | .vs/
27 |
28 | # Gradle cache directory
29 | .gradle/
30 |
31 | # Autogenerated VS/MD/Consulo solution and project files
32 | ExportedObj/
33 | .consulo/
34 | *.csproj
35 | *.unityproj
36 | *.sln
37 | *.suo
38 | *.tmp
39 | *.user
40 | *.userprefs
41 | *.pidb
42 | *.booproj
43 | *.svd
44 | *.pdb
45 | *.mdb
46 | *.opendb
47 | *.VC.db
48 |
49 | # Unity3D generated meta files
50 | *.pidb.meta
51 | *.pdb.meta
52 | *.mdb.meta
53 |
54 | # Unity3D generated file on crash reports
55 | sysinfo.txt
56 |
57 | # Builds
58 | *.apk
59 | *.unitypackage
60 |
61 | # Crashlytics generated file
62 | crashlytics-build.properties
63 |
64 |
--------------------------------------------------------------------------------
/Template/README_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | {PACKAGE.DISPLAYNAME} 👋
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {PACKAGE.DESCRIPTION}
10 |
11 | ## Quick Package Install
12 |
13 | #### Using UnityPackageManager (for Unity 2019.3 or later)
14 | Open the package manager window (menu: Window > Package Manager)
15 | Select "Add package from git URL...", fill in the pop-up with the following link:
16 | {PACKAGE.URL}
17 |
18 | #### Using UnityPackageManager (for Unity 2019.1 or later)
19 |
20 | Find the manifest.json file in the Packages folder of your project and edit it to look like this:
21 | ```js
22 | {
23 | "dependencies": {
24 | "{PACKAGE.NAME}": "{PACKAGE.URL}",
25 | ...
26 | },
27 | }
28 | ```
29 |
30 |
31 |
34 |
35 | ## Usage
36 | {PACKAGE.USAGE}
37 |
38 |
39 |
40 | ## Author
41 |
42 | 👤 **{AUTHOR.NAME}**
43 |
44 | {AUTHOR.SOCIAL}
45 |
46 | ## Show your support
47 |
48 | Give a ⭐️ if this project helped you!
49 |
50 | ***
51 | _This README was generated with ❤️ by [Gameframe.Packages](https://github.com/coryleach/unitypackages)_
52 |
--------------------------------------------------------------------------------
/Editor/PackageMenu.cs:
--------------------------------------------------------------------------------
1 |
2 | using UnityEditor;
3 | using UnityEngine;
4 |
5 | namespace Gameframe.Packages.Editor
6 | {
7 |
8 | public static class PackageMenu
9 | {
10 | [MenuItem("Gameframe/Packages/Create")]
11 | public static void CreateWindow()
12 | {
13 | var window = (PackageMaintainerWindow)EditorWindow.GetWindow(typeof(PackageMaintainerWindow), false, "Package Maintainer");
14 | window.autoRepaintOnSceneChange = true;
15 | window.tab = 2;
16 | }
17 |
18 | [MenuItem("Gameframe/Packages/Maintain")]
19 | public static void MaintainWindow()
20 | {
21 | var window = (PackageMaintainerWindow)EditorWindow.GetWindow(typeof(PackageMaintainerWindow), false, "Package Maintainer");
22 | window.autoRepaintOnSceneChange = true;
23 | window.tab = 0;
24 | }
25 |
26 | [MenuItem("Gameframe/Packages/Embed")]
27 | public static void EmbedWindow()
28 | {
29 | var window = (PackageMaintainerWindow)EditorWindow.GetWindow(typeof(PackageMaintainerWindow), false, "Package Maintainer");
30 | window.autoRepaintOnSceneChange = true;
31 | window.tab = 1;
32 | }
33 |
34 | [MenuItem( "Gameframe/Packages/Documentation/Custom Packages" )]
35 | public static void DocumentationCustomPackages()
36 | {
37 | Application.OpenURL("https://docs.unity3d.com/Manual/CustomPackages.html");
38 | }
39 |
40 | [MenuItem("Gameframe/Packages/Documentation/Readme Markdown")]
41 | public static void ReadmeMarkdown()
42 | {
43 | Application.OpenURL("https://help.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax");
44 | }
45 |
46 |
47 | [MenuItem("Gameframe/Packages/Documentation/Layout Convention")]
48 | public static void DocumentationPackageLayout()
49 | {
50 | Application.OpenURL("https://docs.unity3d.com/Manual/cus-layout.html");
51 | }
52 |
53 | [MenuItem( "Gameframe/Packages/Documentation/Package Manifest" )]
54 | public static void DocumentationPackageManifest()
55 | {
56 | Application.OpenURL("https://docs.unity3d.com/Manual/upm-manifestPkg.html");
57 | }
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Gameframe.Packages 👋
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Package for creating Unity packages just like this one!
10 |
11 | ## Quick Package Install
12 |
13 | #### Using UnityPackageManager (for Unity 2019.3 or later)
14 | Open the package manager window (menu: Window > Package Manager)
15 | Select "Add package from git URL...", fill in the pop-up with the following link:
16 | https://github.com/coryleach/UnityPackages.git#1.0.2
17 |
18 | #### Using UnityPackageManager (for Unity 2019.1 or later)
19 |
20 | Find the manifest.json file in the Packages folder of your project and edit it to look like this:
21 | ```js
22 | {
23 | "dependencies": {
24 | "com.gameframe.packages": "https://github.com/coryleach/UnityPackages.git#1.0.2",
25 | ...
26 | },
27 | }
28 | ```
29 |
30 |
31 |
34 |
35 | ## Usage
36 |
37 | Open the window using the gameframe menu.
38 |
39 | Gameframe->Packages->Maintain
40 | The maintain tab displays and allows you to edit package manifest details
41 | It also has a button for updating the README file.
42 |
43 | Gameframe->Packages->Create
44 | The create tab is used for creating new packages.
45 | You can create packages either embeded in the Unity project or in the chosen source directory.
46 |
47 | Gameframe->Packages->Embed
48 | The embed tap will scan the source directory for packages.
49 | Clicking the 'embed' button on a package will create a softlink to the package in the project's Packages folder.
50 |
51 |
52 |
53 | ## Author
54 |
55 | 👤 **Cory Leach**
56 |
57 | * Twitter: [@coryleach](https://twitter.com/coryleach)
58 | * Github: [@coryleach](https://github.com/coryleach)
59 |
60 |
61 | ## Show your support
62 |
63 | Give a ⭐️ if this project helped you!
64 |
65 | ***
66 | _This README was generated with ❤️ by [Gameframe.Packages](https://github.com/coryleach/unitypackages)_
67 |
--------------------------------------------------------------------------------
/Tests/ShellUtilityTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using NUnit.Framework;
3 | using UnityEngine.TestTools;
4 |
5 | namespace Gameframe.Shell.Tests
6 | {
7 | public class ShellUtilityTests
8 | {
9 | // A Test behaves as an ordinary method
10 | [Test]
11 | public void ExecuteCommand_UseShell([Values(true,false)] bool useShell)
12 | {
13 | var command = "echo \"hello\"";
14 | var result = ShellUtility.ExecuteCommand(command,useShell);
15 | Assert.IsTrue(result,$"Failed to execute command: {command}");
16 | }
17 |
18 | [Test]
19 | public void GetCommandResult_TrimOutput([Values(true,false)] bool trimOutput)
20 | {
21 | var echoText = "hello";
22 | var command = $"echo \"{echoText}\"";
23 | var result = ShellUtility.GetCommandResult(command,trimOutput);
24 |
25 | if (trimOutput)
26 | {
27 | Assert.IsTrue(result == echoText,$"Failed to execute command and get result: {echoText} != {result}");
28 | }
29 | else
30 | {
31 | Assert.IsTrue(result != echoText,$"Failed to execute command and get result: {echoText} != {result}");
32 | Assert.IsTrue(result.StartsWith(echoText),$"Failed to execute command and get result: {echoText} != {result}");
33 | }
34 | }
35 |
36 | [UnityTest]
37 | public IEnumerator ExecuteCommandAsync_UseShell([Values(true,false)] bool useShell)
38 | {
39 | var command = "echo \"hello\"";
40 | var task = ShellUtility.ExecuteCommandAsync(command,useShell);
41 |
42 | while (!task.IsCompleted)
43 | {
44 | yield return null;
45 | }
46 |
47 | var result = task.Result;
48 | Assert.IsTrue(result,$"Failed to execute command: {command}");
49 | }
50 |
51 | [UnityTest]
52 | public IEnumerator GetCommandResultAsync_TrimOutput([Values(true,false)] bool trimOutput)
53 | {
54 | var echoText = "hello";
55 | var command = $"echo \"{echoText}\"";
56 | var task = ShellUtility.GetCommandResultAsync(command,trimOutput);
57 |
58 | while (!task.IsCompleted)
59 | {
60 | yield return null;
61 | }
62 |
63 | var result = task.Result;
64 |
65 | if (trimOutput)
66 | {
67 | Assert.IsTrue(result == echoText,$"Failed to execute command and get result: {echoText} != {result}");
68 | }
69 | else
70 | {
71 | Assert.IsTrue(result != echoText,$"Failed to execute command and get result: {echoText} != {result}");
72 | Assert.IsTrue(result.StartsWith(echoText),$"Failed to execute command and get result: {echoText} != {result}");
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Documentation/GameframeFace.gif.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3c95fc43cb07b4db9befe7e733e5e938
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 12
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 1
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | vTOnly: 0
27 | ignoreMasterTextureLimit: 0
28 | grayScaleToAlpha: 0
29 | generateCubemap: 6
30 | cubemapConvolution: 0
31 | seamlessCubemap: 0
32 | textureFormat: 1
33 | maxTextureSize: 2048
34 | textureSettings:
35 | serializedVersion: 2
36 | filterMode: 1
37 | aniso: 1
38 | mipBias: 0
39 | wrapU: 0
40 | wrapV: 0
41 | wrapW: 0
42 | nPOTScale: 1
43 | lightmap: 0
44 | compressionQuality: 50
45 | spriteMode: 0
46 | spriteExtrude: 1
47 | spriteMeshType: 1
48 | alignment: 0
49 | spritePivot: {x: 0.5, y: 0.5}
50 | spritePixelsToUnits: 100
51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
52 | spriteGenerateFallbackPhysicsShape: 1
53 | alphaUsage: 1
54 | alphaIsTransparency: 0
55 | spriteTessellationDetail: -1
56 | textureType: 0
57 | textureShape: 1
58 | singleChannelComponent: 0
59 | flipbookRows: 1
60 | flipbookColumns: 1
61 | maxTextureSizeSet: 0
62 | compressionQualitySet: 0
63 | textureFormatSet: 0
64 | ignorePngGamma: 0
65 | applyGammaDecoding: 0
66 | cookieLightType: 0
67 | platformSettings:
68 | - serializedVersion: 3
69 | buildTarget: DefaultTexturePlatform
70 | maxTextureSize: 2048
71 | resizeAlgorithm: 0
72 | textureFormat: -1
73 | textureCompression: 1
74 | compressionQuality: 50
75 | crunchedCompression: 0
76 | allowsAlphaSplitting: 0
77 | overridden: 0
78 | androidETC2FallbackOverride: 0
79 | forceMaximumCompressionQuality_BC6H_BC7: 0
80 | - serializedVersion: 3
81 | buildTarget: Standalone
82 | maxTextureSize: 2048
83 | resizeAlgorithm: 0
84 | textureFormat: -1
85 | textureCompression: 1
86 | compressionQuality: 50
87 | crunchedCompression: 0
88 | allowsAlphaSplitting: 0
89 | overridden: 0
90 | androidETC2FallbackOverride: 0
91 | forceMaximumCompressionQuality_BC6H_BC7: 0
92 | - serializedVersion: 3
93 | buildTarget: Server
94 | maxTextureSize: 2048
95 | resizeAlgorithm: 0
96 | textureFormat: -1
97 | textureCompression: 1
98 | compressionQuality: 50
99 | crunchedCompression: 0
100 | allowsAlphaSplitting: 0
101 | overridden: 0
102 | androidETC2FallbackOverride: 0
103 | forceMaximumCompressionQuality_BC6H_BC7: 0
104 | spriteSheet:
105 | serializedVersion: 2
106 | sprites: []
107 | outline: []
108 | physicsShape: []
109 | bones: []
110 | spriteID:
111 | internalID: 0
112 | vertices: []
113 | indices:
114 | edges: []
115 | weights: []
116 | secondaryTextures: []
117 | nameFileIdTable: {}
118 | spritePackingTag:
119 | pSDRemoveMatte: 0
120 | pSDShowRemoveMatteOption: 0
121 | userData:
122 | assetBundleName:
123 | assetBundleVariant:
124 |
--------------------------------------------------------------------------------
/Editor/PackageUtility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 |
4 | namespace Gameframe.Packages
5 | {
6 | public static class PackageUtility
7 | {
8 | private const string StartDocTag = "";
9 | private const string EndDocTag = "";
10 |
11 | public static string ExtractText(string text)
12 | {
13 | var startIndex = text.IndexOf(StartDocTag, StringComparison.Ordinal) + StartDocTag.Length;
14 | var endIndex = text.IndexOf(EndDocTag, startIndex, StringComparison.Ordinal);
15 |
16 | if (startIndex == -1 || endIndex == -1)
17 | {
18 | return string.Empty;
19 | }
20 |
21 | return text.Substring(startIndex, endIndex - startIndex);
22 | }
23 |
24 | public static string PatchReadmeText(string oldReadmeText, string templateText)
25 | {
26 | //Extract Documenation from old readme
27 | oldReadmeText = ExtractText(oldReadmeText);
28 | //Find the replace-able text in the template
29 | var replaceableText = ExtractText(templateText);
30 | //Insert old readme text into the template
31 | return templateText.Replace(replaceableText, oldReadmeText);
32 | }
33 |
34 | public static string CreateLicenseText(string licenseText, PackageManifest packageManifest)
35 | {
36 | var licenseBuilder = new StringBuilder(licenseText);
37 | licenseBuilder.Replace("{DATE.YEAR}",DateTime.Now.Year.ToString());
38 | licenseBuilder.Replace("{AUTHOR.NAME}",packageManifest.author.name);
39 | return licenseBuilder.ToString();
40 | }
41 |
42 | public static string GithubUrl(string username)
43 | {
44 | return $"https://github.com/{username}";
45 | }
46 |
47 | public static string TwitterUrl(string username)
48 | {
49 | return $"https://twitter.com/{username}";
50 | }
51 |
52 | public static string PackageUrl(string github, string repository, string version)
53 | {
54 | return $"https://github.com/{github}/{repository}.git#{version}";
55 | }
56 |
57 | public static string CreateReadmeText(string text, PackageManifest packageManifest)
58 | {
59 | var description = packageManifest.description;
60 |
61 | //Replace line endings so that text in the readme line breaks properly
62 | description = description.Replace("\r\n", "\n");
63 | description = description.Replace("\n", " \n");
64 |
65 | var readmeText = new StringBuilder(text);
66 | readmeText.Replace("{TWITTER.USERNAME}",packageManifest.author.twitter);
67 | readmeText.Replace("{AUTHOR.TWITTER}",packageManifest.author.name);
68 | readmeText.Replace("{AUTHOR.NAME}",packageManifest.author.name);
69 | readmeText.Replace("{GITHUB.USERNAME}",packageManifest.author.github);
70 | readmeText.Replace("{PACKAGE.VERSION}",packageManifest.version);
71 | readmeText.Replace("{PACKAGE.DESCRIPTION}",description);
72 | readmeText.Replace("{PACKAGE.DISPLAYNAME}",packageManifest.displayName);
73 | readmeText.Replace("{PACKAGE.NAME}",packageManifest.name);
74 | readmeText.Replace("{PACKAGE.USAGE}","TODO: Write Usage Documentation Here");
75 | readmeText.Replace("{PACKAGE.URL}", PackageUrl(packageManifest.author.github, packageManifest.repositoryName, packageManifest.version));
76 |
77 | var social = new StringBuilder();
78 | if (!string.IsNullOrEmpty(packageManifest.author.twitter))
79 | {
80 | social.AppendLine($"* Twitter: [@{packageManifest.author.twitter}]({TwitterUrl(packageManifest.author.twitter)})");
81 | }
82 | if (!string.IsNullOrEmpty(packageManifest.author.github))
83 | {
84 | social.AppendLine($"* Github: [@{packageManifest.author.github}]({GithubUrl(packageManifest.author.github)})");
85 | }
86 | readmeText.Replace("{AUTHOR.SOCIAL}", social.ToString());
87 |
88 | return readmeText.ToString();
89 | }
90 |
91 | }
92 | }
93 |
94 |
95 |
--------------------------------------------------------------------------------
/Editor/ShellUtility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading.Tasks;
4 |
5 | namespace Gameframe.Shell
6 | {
7 | public static class ShellUtility
8 | {
9 | ///
10 | /// Executes a command in the system shell.
11 | /// Windows uses: powershell.exe by default. Use ExecuteWindowsCommand for cmd.exe
12 | /// Mac uses: /bin/bash
13 | ///
14 | /// Command to execute
15 | /// If true we'll use the OS shell. Otherwise command will execute as its own process.
16 | /// True if command exited with code 0 otherwise returns false
17 | public static bool ExecuteCommand(string command, bool useShell = false)
18 | {
19 | #if UNITY_EDITOR_WIN
20 | var commandBytes = System.Text.Encoding.Unicode.GetBytes(command);
21 | var encodedCommand = Convert.ToBase64String(commandBytes);
22 | var processInfo = new ProcessStartInfo("powershell.exe", $"-EncodedCommand {encodedCommand}")
23 | {
24 | CreateNoWindow = true,
25 | UseShellExecute = useShell,
26 | RedirectStandardOutput = !useShell,
27 | WindowStyle = ProcessWindowStyle.Hidden
28 | };
29 | #else
30 | var processInfo = new ProcessStartInfo("/bin/bash", $"-c \"{command.Replace("\\","\\\\")}\"")
31 | {
32 | CreateNoWindow = true,
33 | UseShellExecute = useShell,
34 | RedirectStandardOutput = !useShell,
35 | WindowStyle = ProcessWindowStyle.Hidden
36 | };
37 | #endif
38 |
39 | var process = Process.Start(processInfo);
40 | if (process == null)
41 | {
42 | return false;
43 | }
44 |
45 | process.WaitForExit();
46 | var exitCode = process.ExitCode;
47 | process.Close();
48 |
49 | return exitCode == 0;
50 | }
51 |
52 | ///
53 | /// Execute command using the basic windows shell cmd.exe
54 | ///
55 | /// command to be executed
56 | /// true if successfull. false otherwise.
57 | public static bool ExecuteWindowsCommand(string command)
58 | {
59 | command = $"/c {command}";
60 | var processInfo = new ProcessStartInfo("cmd.exe", command.Replace("\\","\\\\"))
61 | {
62 | CreateNoWindow = true,
63 | Verb = "runas",
64 | WindowStyle = ProcessWindowStyle.Hidden
65 | };
66 |
67 | var process = Process.Start(processInfo);
68 | if (process == null)
69 | {
70 | return false;
71 | }
72 |
73 | process.WaitForExit();
74 | var exitCode = process.ExitCode;
75 | process.Close();
76 |
77 | return exitCode == 0;
78 | }
79 |
80 | ///
81 | /// Get the string output of a command in the shell
82 | ///
83 | /// Command string to be run
84 | /// True if we should trim new line and line feed from end of output stream
85 | /// String output of the command.
86 | public static string GetCommandResult(string command, bool trimOutput = true)
87 | {
88 | #if UNITY_EDITOR_WIN
89 | var commandBytes = System.Text.Encoding.Unicode.GetBytes(command);
90 | var encodedCommand = Convert.ToBase64String(commandBytes);
91 | var processInfo = new ProcessStartInfo("powershell.exe", $"-EncodedCommand {encodedCommand}")
92 | {
93 | CreateNoWindow = true,
94 | UseShellExecute = false,
95 | RedirectStandardOutput = true
96 | };
97 | #else
98 | var processInfo = new ProcessStartInfo("/bin/bash", $"-c \"{command.Replace("\\","\\\\")}\"")
99 | {
100 | CreateNoWindow = true,
101 | UseShellExecute = false,
102 | RedirectStandardOutput = true,
103 | WindowStyle = ProcessWindowStyle.Hidden
104 | };
105 | #endif
106 |
107 | var process = Process.Start(processInfo);
108 | if (process == null)
109 | {
110 | return null;
111 | }
112 |
113 | var output = process.StandardOutput.ReadToEnd();
114 | process.WaitForExit();
115 |
116 | var exitCode = process.ExitCode;
117 | process.Close();
118 |
119 | if (exitCode != 0)
120 | {
121 | return null;
122 | }
123 |
124 | if (trimOutput)
125 | {
126 | output = output.TrimEnd('\n','\r');
127 | }
128 |
129 | return output;
130 | }
131 |
132 | ///
133 | /// Run a command and get the string output as a task
134 | ///
135 | /// Command string to run.
136 | /// Trims any new line characters from the end of the output
137 | /// Task that results in a string containing the output of the command. OR null if command failed to execute.
138 | public static async Task GetCommandResultAsync(string command, bool trimOutput = true)
139 | {
140 | var task = Task.Run(() => GetCommandResult(command,trimOutput));
141 | await task;
142 | return task.Result;
143 | }
144 |
145 | ///
146 | /// Execute a shell command as a task
147 | ///
148 | /// command string to execute
149 | /// If true the command will execute in a system shell.
150 | /// Task with a result of true when command exits with code 0.
151 | public static async Task ExecuteCommandAsync(string command, bool useShell = false)
152 | {
153 | var task = Task.Run(() => ExecuteCommand(command, useShell));
154 | await task;
155 | return task.Result;
156 | }
157 |
158 | ///
159 | /// Executes a command to create a symbolic link
160 | ///
161 | /// Source file path
162 | /// Destination file path
163 | /// set this to true if you're creating a link for a directory
164 | /// true if the command is a success. false on failure.
165 | public static bool CreateSymbolicLink(string source, string destination)
166 | {
167 | #if UNITY_EDITOR_WIN
168 | return ExecuteWindowsCommand($"mklink /D \"{destination}\" \"{source}\"");
169 | #else
170 | return ExecuteCommand($"ln -s {source} {destination}");
171 | #endif
172 | }
173 |
174 | }
175 | }
--------------------------------------------------------------------------------
/Editor/PackageNpmPublisher/PackageNpmPublisher.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Threading.Tasks;
4 | using Gameframe.Shell;
5 | using UnityEditor;
6 | using UnityEditor.UIElements;
7 | using UnityEngine;
8 | using UnityEngine.UIElements;
9 | using Debug = UnityEngine.Debug;
10 |
11 | namespace Gameframe.Packages.Editor
12 | {
13 | public class PackageNpmPublisher : EditorWindow
14 | {
15 | public string address = "npm.coryleach.info:4873";
16 | public string username = "coryleach";
17 | public string email = "cory.leach@gmail.com";
18 | public string password = "";
19 |
20 | private List selectedPackageList = new List();
21 | private ScrollView packageScrollList;
22 | private Label loginStatusLabel;
23 |
24 | private static string ResourcePath = "Packages/com.gameframe.packages/Editor/PackagePublisherWindow/";
25 |
26 | private InstallStatus installStatus = InstallStatus.Unknown;
27 |
28 | private const string StyleSheetFilename = "PackageNpmPublisher.uss";
29 |
30 | public enum InstallStatus
31 | {
32 | Unknown,
33 | Installed,
34 | NotFound
35 | }
36 |
37 | //Commenting this out.
38 | //I have no plans to continue to support this for now
39 | /*[MenuItem("Gameframe/Packages/Publisher")]
40 | public static void Open()
41 | {
42 | PackageNpmPublisher wnd = GetWindow();
43 | wnd.titleContent = new GUIContent("Package Publisher");
44 | }*/
45 |
46 | public void OnEnable()
47 | {
48 | // Each editor window contains a root VisualElement object
49 | VisualElement root = rootVisualElement;
50 |
51 | // A stylesheet can be added to a VisualElement.
52 | // The style will be applied to the VisualElement and all of its children.
53 | var styleSheetPath = ResourcePath + StyleSheetFilename;
54 | var styleSheet = AssetDatabase.LoadAssetAtPath(styleSheetPath);
55 | if ( styleSheet != null )
56 | {
57 | root.styleSheets.Add(styleSheet);
58 | }
59 | else
60 | {
61 | Debug.LogError($"Failed to Load Style Sheet. Check uss file exists or syntax error.");
62 | }
63 |
64 | // Import UXML
65 | //var visualTree = AssetDatabase.LoadAssetAtPath("Packages/com.gameframe.packages/Editor/PackagePublisherWindow/PackagePublisher.uxml");
66 | //VisualElement labelFromUXML = visualTree.CloneTree();
67 | //root.Add(labelFromUXML);
68 | SerializedObject so = new SerializedObject(this);
69 |
70 | var fieldContainer = new VisualElement()
71 | {
72 | name = "FieldContainer"
73 | };
74 | fieldContainer.AddToClassList("BorderedContainer");
75 | root.Add(fieldContainer);
76 |
77 | var serverField = new TextField
78 | {
79 | label = "Server",
80 | bindingPath = nameof(address)
81 | };
82 | serverField.Bind(so);
83 | fieldContainer.Add(serverField);
84 |
85 | var emailField = new TextField
86 | {
87 | label = "email",
88 | bindingPath = nameof(email)
89 | };
90 | emailField.Bind(so);
91 | fieldContainer.Add(emailField);
92 |
93 | var usernameField = new TextField
94 | {
95 | label = "Username",
96 | bindingPath = nameof(username),
97 | };
98 | usernameField.Bind(so);
99 | fieldContainer.Add(usernameField);
100 |
101 | var passwordField = new TextField()
102 | {
103 | label = "Password",
104 | bindingPath = nameof(password),
105 | isPasswordField = true
106 | };
107 | passwordField.Bind(so);
108 | fieldContainer.Add(passwordField);
109 |
110 | packageScrollList = new ScrollView();
111 | packageScrollList.AddToClassList("BorderedContainer");
112 | root.Add(packageScrollList);
113 |
114 | var buttonContainer = new VisualElement()
115 | {
116 | name = "ButtonContainer",
117 | style =
118 | {
119 | flexDirection = FlexDirection.Row,
120 | justifyContent = Justify.Center,
121 | }
122 | };
123 | root.Add(buttonContainer);
124 |
125 | loginStatusLabel = new Label("Waiting...")
126 | {
127 | style =
128 | {
129 | unityTextAlign = TextAnchor.MiddleRight
130 | }
131 | };
132 | fieldContainer.Add(loginStatusLabel);
133 |
134 | var loginButton = new Button(Login)
135 | {
136 | name = "ButtonLogin",
137 | text = "Login",
138 | style =
139 | {
140 | marginTop = 5,
141 | marginLeft = 5,
142 | marginRight = 5,
143 | paddingTop = 4
144 | }
145 | };
146 | fieldContainer.Add(loginButton);
147 |
148 | var publishButton = new Button(Publish)
149 | {
150 | name = "ButtonPublish",
151 | text = "Publish",
152 | style =
153 | {
154 | marginLeft = 5,
155 | marginRight = 5
156 | }
157 | };
158 | buttonContainer.Add(publishButton);
159 |
160 | var refreshButton = new Button(Refresh)
161 | {
162 | name = "ButtonRefresh",
163 | text = "Refresh",
164 | style =
165 | {
166 | marginLeft = 5,
167 | marginRight = 5
168 | }
169 | };
170 | buttonContainer.Add(refreshButton);
171 |
172 | /*var installButton = new Button(InstallCli)
173 | {
174 | name = "ButtonInstall",
175 | style =
176 | {
177 | marginLeft = 5,
178 | marginRight = 5
179 | }
180 | };
181 | installButton.Add(new Label("Install"));
182 | buttonContainer.Add(installButton);*/
183 |
184 | selectedPackageList = new List();
185 | PopulateScrollViewWithPackages(packageScrollList);
186 |
187 | CheckLogin();
188 | CheckInstall();
189 | }
190 |
191 | private async void CheckInstall()
192 | {
193 | installStatus = InstallStatus.Unknown;
194 | var checkTask = await ShellUtility.ExecuteCommandAsync("npm version");
195 | installStatus = !checkTask ? InstallStatus.NotFound : InstallStatus.Installed;
196 | }
197 |
198 | private async void CheckLogin()
199 | {
200 | loginStatusLabel.text = "Waiting...";
201 | var checkTask = ShellUtility.ExecuteCommandAsync("npm whoami");
202 | await checkTask;
203 | loginStatusLabel.text = !checkTask.Result ? "Disconnected" : "Connected";
204 | }
205 |
206 | /*private void InstallCli()
207 | {
208 | Debug.Log("Installing...");
209 | ExecuteShellCommand("npm install -g npm-cli-login publish");
210 | }
211 |
212 | private void UpdateVersions()
213 | {
214 | var directories = Directory.GetDirectories("Packages/");
215 | foreach (var directory in directories)
216 | {
217 | var filename = $"{directory}/package.json";
218 | var json = File.ReadAllText(filename);
219 |
220 | var match = Regex.Match(json, @"""version"": ""(\d+\.)(\d+\.)(\d+)""");
221 | match = Regex.Match(match.Value, @"(\d+\.)(\d+\.)(\d+)");
222 | var split = match.Value.Split('.');
223 | var buildNumber = int.Parse(split[split.Length - 1]);
224 | buildNumber += 1;
225 |
226 | var newVersion = $"\"version\": \"{split[0]}.{split[1]}.{buildNumber}\"";
227 | var updatedJson = Regex.Replace(json, @"""version"": ""(\d+\.)(\d+\.)(\d+)""", newVersion);
228 |
229 | File.WriteAllText(filename, updatedJson);
230 | }
231 | }*/
232 |
233 | #region Commands
234 |
235 | private void Refresh()
236 | {
237 | packageScrollList.Clear();
238 | PopulateScrollViewWithPackages(packageScrollList);
239 | }
240 |
241 | private async void Publish()
242 | {
243 | if (selectedPackageList.Count == 0)
244 | {
245 | Debug.Log("No selected packages to publish");
246 | return;
247 | }
248 |
249 | foreach (var package in selectedPackageList)
250 | {
251 | //publishing
252 | Debug.Log($"Publishing {package.displayName} {package.version}");
253 | var cmd = $"npm publish Packages/{package.name} --registry http://{address}";
254 | var task = ShellUtility.ExecuteCommandAsync(cmd);
255 | await task;
256 | if (!task.Result)
257 | {
258 | Debug.LogError($"Failed to publish {package.displayName}");
259 | }
260 | else
261 | {
262 | Debug.Log($"Published {package.displayName}");
263 | }
264 | }
265 |
266 | Refresh();
267 | }
268 |
269 | private async void Login()
270 | {
271 | loginStatusLabel.text = "Waiting...";
272 |
273 | var cmd = $"npm-cli-login -u {username} -p {password} -e {email} -r http://{address}";
274 | var task = ShellUtility.ExecuteCommandAsync(cmd);
275 | await task;
276 | if (!task.Result)
277 | {
278 | Debug.Log("Failed login");
279 | }
280 |
281 | cmd = $"npm config set registry http://{address}";
282 | task = ShellUtility.ExecuteCommandAsync(cmd);
283 | await task;
284 | if (!task.Result)
285 | {
286 | Debug.Log("Failed to set registry address");
287 | }
288 |
289 | CheckLogin();
290 | }
291 |
292 | #endregion
293 |
294 | private static List GetLocalPackageList()
295 | {
296 | var packageList = new List();
297 | var directories = Directory.GetDirectories("Packages/");
298 | foreach (var directory in directories)
299 | {
300 | var packageManifestPath = $"{directory}/package.json";
301 | //publishing
302 | if (!File.Exists(packageManifestPath))
303 | {
304 | continue;
305 | }
306 |
307 | var json = File.ReadAllText(packageManifestPath);
308 | var packageManifest = JsonUtility.FromJson(json);
309 | if (packageManifest != null)
310 | {
311 | packageList.Add(packageManifest);
312 | }
313 | }
314 | return packageList;
315 | }
316 |
317 | private async Task> GetLocalPackageListAsync()
318 | {
319 | var task = Task.Run(GetLocalPackageListAsync);
320 | await task;
321 | return task.Result;
322 | }
323 |
324 | private async void PopulateScrollViewWithPackages(ScrollView scrollView)
325 | {
326 | var packages = GetLocalPackageListAsync();
327 | await packages;
328 | int row = 0;
329 | foreach (var package in packages.Result)
330 | {
331 | var currentPackage = package;
332 | var toggle = new Toggle
333 | {
334 | label = $"{currentPackage.displayName}:{currentPackage.version}",
335 | };
336 | toggle.AddToClassList(row % 2 == 0 ? "rowEven" : "rowOdd");
337 | toggle.labelElement.style.flexGrow = 100;
338 | toggle.RegisterValueChangedCallback((value) =>
339 | {
340 | if (value.newValue)
341 | {
342 | selectedPackageList.Add(currentPackage);
343 | }
344 | else
345 | {
346 | selectedPackageList.Remove(currentPackage);
347 | }
348 | });
349 | scrollView.Add(toggle);
350 | row += 1;
351 |
352 | SetToggleRemoteVersionAsync(toggle,package);
353 | }
354 | }
355 |
356 | private static async void SetToggleRemoteVersionAsync(Toggle toggle, PackageManifest package)
357 | {
358 | PackageManifest remotePackage = null;
359 |
360 | var task = ShellUtility.GetCommandResultAsync($"npm view {package.name} --json");
361 | await task;
362 | var json = task.Result;
363 |
364 | if (json != null)
365 | {
366 | remotePackage = JsonUtility.FromJson(json);
367 | }
368 |
369 | toggle.label = remotePackage == null ? $"{toggle.label} (not found)" : $"{toggle.label} ({remotePackage.version})";
370 | toggle.SetEnabled(remotePackage == null || remotePackage.version != package.version);
371 | toggle.value = false;
372 | }
373 |
374 | }
375 | }
376 |
--------------------------------------------------------------------------------
/Editor/PackageMaintainerWindow.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Gameframe.Packages.Editor;
7 | using Gameframe.Packages.Utility;
8 | using Gameframe.Shell;
9 | using UnityEditor;
10 | using UnityEditor.PackageManager;
11 | using UnityEngine;
12 | using PackageInfo = UnityEditor.PackageManager.PackageInfo;
13 |
14 | namespace Gameframe.Packages
15 | {
16 | public class PackageMaintainerWindow : EditorWindow
17 | {
18 |
19 | public PackageManifest packageManifest = new PackageManifest();
20 | public List embededPackages = new List();
21 | public int selectedPackageIndex = 0;
22 | public string[] packageNames = new string[0];
23 | public List sourcePackages = new List();
24 | public int tab = 0;
25 | public bool displayName = true;
26 |
27 | private ScriptableObject target = null;
28 | private SerializedObject serializedObject = null;
29 | private Vector2 scrollPt = Vector2.zero;
30 | private readonly string[] toolbar = {"Maintain", "Embed", "Create"};
31 |
32 | private void OnEnable()
33 | {
34 | target = this;
35 | serializedObject = new SerializedObject(target);
36 | packageManifest = PackageSettings.Manifest;
37 | Refresh();
38 | }
39 |
40 | private void OnProjectChange()
41 | {
42 | Refresh();
43 | }
44 |
45 | private void UpdateSourcePackages()
46 | {
47 | var sourcePath = PackageSettings.SourcePath;
48 | if (string.IsNullOrEmpty(sourcePath))
49 | {
50 | return;
51 | }
52 |
53 | sourcePackages.Clear();
54 |
55 | var directories = Directory.GetDirectories(sourcePath);
56 | foreach (var directory in directories)
57 | {
58 | //Check each directory for a package manifest
59 | if (File.Exists($"{directory}/package.json"))
60 | {
61 | var pkgInfo = new SourcePackageInfo(directory);
62 | sourcePackages.Add(pkgInfo);
63 | }
64 | }
65 |
66 | SortPackageList();
67 | CheckEmbededStatus();
68 | }
69 |
70 | private void CheckEmbededStatus()
71 | {
72 | foreach (var sourcePackage in sourcePackages)
73 | {
74 | if (sourcePackage.status == SourcePackageInfo.Status.Error)
75 | {
76 | continue;
77 | }
78 |
79 | sourcePackage.status = embededPackages.Any(x => x.name == sourcePackage.packageInfo.name)
80 | ? SourcePackageInfo.Status.Embeded
81 | : SourcePackageInfo.Status.NotEmbeded;
82 | }
83 | }
84 |
85 | private async void UpdateEmbeddedPackages()
86 | {
87 | var request = Client.List();
88 |
89 | await request;
90 |
91 | if (request.Status == StatusCode.InProgress)
92 | {
93 | Debug.LogError("Failed to await package list response.");
94 | return;
95 | }
96 |
97 | if (request.Status == StatusCode.Failure)
98 | {
99 | Debug.LogError($"Get Packages Failed: {request.Error.errorCode} {request.Error.message}");
100 | return;
101 | }
102 |
103 | //Get All Embeded Packages
104 | embededPackages = request.Result.Where(x => x.source == PackageSource.Embedded).ToList();
105 | packageNames = embededPackages.Select(x => x.displayName).ToArray();
106 | CheckEmbededStatus();
107 | EditorUtility.SetDirty(this);
108 | }
109 |
110 | private async Task GetMyPackageInfoAsync()
111 | {
112 | var request = Client.List();
113 | await request;
114 | var result = request.Result.First(x => x.name == "com.gameframe.packages");
115 | return result;
116 | }
117 |
118 | private void Refresh()
119 | {
120 | UpdateEmbeddedPackages();
121 | UpdateSourcePackages();
122 | }
123 |
124 | private void OnGUI()
125 | {
126 | serializedObject.Update();
127 | tab = GUILayout.Toolbar(tab, toolbar);
128 | switch (tab)
129 | {
130 | case 0:
131 | MaintainPackageGUI();
132 | break;
133 | case 1:
134 | EmbedPackageGUI();
135 | break;
136 | case 2:
137 | CreatePackageGUI();
138 | break;
139 | }
140 | serializedObject.ApplyModifiedProperties();
141 | }
142 |
143 | private void SortPackageList()
144 | {
145 | if (displayName)
146 | {
147 | sourcePackages.Sort((a,b)=>string.Compare(a.packageInfo.displayName, b.packageInfo.displayName, StringComparison.Ordinal));
148 | }
149 | else
150 | {
151 | sourcePackages.Sort((a,b)=>string.Compare(a.packageInfo.name, b.packageInfo.name, StringComparison.Ordinal));
152 | }
153 | }
154 |
155 | private void EmbedPackageGUI()
156 | {
157 | if (PackageGuiUtility.SourcePathGui())
158 | {
159 | Refresh();
160 | }
161 |
162 | EditorGUI.BeginChangeCheck();
163 | displayName = EditorGUILayout.Toggle("ShowDisplayName", displayName);
164 | if (EditorGUI.EndChangeCheck())
165 | {
166 | SortPackageList();
167 | }
168 |
169 | bool refreshAssets = false;
170 |
171 | scrollPt = EditorGUILayout.BeginScrollView(scrollPt, GUILayout.ExpandHeight(false));
172 | foreach (var sourcePkg in sourcePackages)
173 | {
174 | EditorGUILayout.BeginHorizontal("box");
175 |
176 | if (displayName)
177 | {
178 | EditorGUILayout.LabelField(sourcePkg.packageInfo?.displayName);
179 | }
180 | else
181 | {
182 | EditorGUILayout.LabelField(sourcePkg.packageInfo?.name);
183 | }
184 |
185 | if (sourcePkg.status == SourcePackageInfo.Status.Error)
186 | {
187 | EditorGUILayout.LabelField("Error", GUILayout.Width(60));
188 | }
189 | else if (sourcePkg.status == SourcePackageInfo.Status.Embeded)
190 | {
191 | EditorGUILayout.LabelField("Embeded", GUILayout.Width(60));
192 | }
193 | else if (GUILayout.Button("Embed", GUILayout.Width(60)))
194 | {
195 | try
196 | {
197 | //Create a softlink to the source package in our local package directory
198 | var source = sourcePkg.directoryInfo.FullName;
199 | var dest = $"{Application.dataPath}/../Packages/{sourcePkg.directoryInfo.Name}";
200 | if (!ShellUtility.CreateSymbolicLink(source, dest))
201 | {
202 | EditorApplication.Beep();
203 | EditorUtility.DisplayDialog("Package Embed", "Failed to create symbolic link. Package was not embedded.",
204 | "OK");
205 | }
206 | else
207 | {
208 | refreshAssets = true;
209 | EditorUtility.DisplayDialog("Package Embed", "Done",
210 | "OK");
211 | }
212 | }
213 | catch ( Exception e )
214 | {
215 | Debug.LogException(e);
216 | }
217 | }
218 | EditorGUILayout.EndHorizontal();
219 | }
220 | EditorGUILayout.EndScrollView();
221 |
222 | //Doing this outside of the foreach loop to avoid layout and enum errors
223 | if (refreshAssets)
224 | {
225 | AssetDatabase.Refresh();
226 | GUIUtility.ExitGUI();
227 | }
228 |
229 | RefreshGUI();
230 | }
231 |
232 | private PackageManifest maintainPackageManifest = null;
233 |
234 | private void MaintainPackageGUI()
235 | {
236 | EditorGUI.BeginChangeCheck();
237 |
238 | selectedPackageIndex = EditorGUILayout.Popup("Package", selectedPackageIndex, packageNames);
239 | if (EditorGUI.EndChangeCheck())
240 | {
241 | //Index Changed
242 | maintainPackageManifest = null;
243 | }
244 |
245 | //Validate index
246 | if (selectedPackageIndex < 0 || selectedPackageIndex >= embededPackages.Count)
247 | {
248 | return;
249 | }
250 |
251 | var package = embededPackages[selectedPackageIndex];
252 |
253 | //Need to get the json for the package
254 | if (maintainPackageManifest == null)
255 | {
256 | var json = File.ReadAllText($"{package.assetPath}/package.json");
257 | maintainPackageManifest = JsonUtility.FromJson(json);
258 | }
259 |
260 | var rect = EditorGUILayout.BeginVertical("box");
261 | EditorGUILayout.LabelField("Name",package.name);
262 | EditorGUILayout.LabelField("DisplayName",package.displayName);
263 | EditorGUILayout.LabelField("Source",package.source.ToString());
264 | EditorGUILayout.LabelField("Asset Path",package.assetPath);
265 | EditorGUILayout.LabelField("Resolved Path",package.resolvedPath);
266 | EditorGUILayout.LabelField("Type",package.type);
267 | EditorGUILayout.LabelField("Version",package.version);
268 | EditorGUILayout.LabelField("Status",package.status.ToString());
269 | EditorGUILayout.EndVertical();
270 |
271 | if (Event.current.type == EventType.MouseUp && Event.current.button == 0 && rect.Contains(Event.current.mousePosition))
272 | {
273 | var asset = AssetDatabase.LoadAssetAtPath($"{package.assetPath}/package.json");
274 | Selection.activeObject = asset;
275 | }
276 |
277 | EditorGUILayout.BeginVertical("box");
278 | maintainPackageManifest.repositoryName = EditorGUILayout.TextField("RepositoryName", maintainPackageManifest.repositoryName);
279 | maintainPackageManifest.author.name = EditorGUILayout.TextField("Author Name",maintainPackageManifest.author.name);
280 | maintainPackageManifest.author.email = EditorGUILayout.TextField("Author E-Mail",maintainPackageManifest.author.email);
281 | maintainPackageManifest.author.url = EditorGUILayout.TextField("Author URL",maintainPackageManifest.author.url);
282 |
283 | maintainPackageManifest.author.twitter = EditorGUILayout.TextField("Twitter",maintainPackageManifest.author.twitter);
284 | maintainPackageManifest.author.github = EditorGUILayout.TextField("GitHub",maintainPackageManifest.author.github);
285 |
286 | var linkStyle = new GUIStyle(EditorStyles.label);
287 | linkStyle.wordWrap = false;
288 | linkStyle.hover.textColor = new Color(0x00 / 255f, 0x78 / 255f, 0xDA / 255f, 1f);
289 | linkStyle.normal.textColor = new Color(0,0,1);
290 |
291 | if (!string.IsNullOrEmpty(maintainPackageManifest.author.twitter))
292 | {
293 | var twitterUrl = PackageUtility.TwitterUrl(maintainPackageManifest.author.twitter);
294 | if (GUILayout.Button(twitterUrl,linkStyle))
295 | {
296 | Application.OpenURL(twitterUrl);
297 | }
298 | }
299 |
300 | if (!string.IsNullOrEmpty(maintainPackageManifest.author.github))
301 | {
302 | var githubUrl = PackageUtility.GithubUrl(maintainPackageManifest.author.github);
303 | if (GUILayout.Button(githubUrl, linkStyle))
304 | {
305 | Application.OpenURL(githubUrl);
306 | }
307 | var packageUrl = PackageUtility.PackageUrl(maintainPackageManifest.author.github,maintainPackageManifest.repositoryName,maintainPackageManifest.version);
308 | if (GUILayout.Button(packageUrl, linkStyle))
309 | {
310 | Application.OpenURL(packageUrl);
311 | }
312 | }
313 |
314 | EditorGUILayout.EndVertical();
315 |
316 | if (GUILayout.Button("Update package.json"))
317 | {
318 | var path = $"{package.assetPath}/package.json";
319 | var json = File.ReadAllText(path);
320 | var jsonNode = SimpleJSON.JSON.Parse(json);
321 | jsonNode["repositoryName"] = maintainPackageManifest.repositoryName;
322 | jsonNode["author"]["name"] = maintainPackageManifest.author.name;
323 | jsonNode["author"]["email"] = maintainPackageManifest.author.email;
324 | jsonNode["author"]["url"] = maintainPackageManifest.author.url;
325 | jsonNode["author"]["github"] = maintainPackageManifest.author.github;
326 | jsonNode["author"]["twitter"] = maintainPackageManifest.author.twitter;
327 | File.WriteAllText(path,jsonNode.ToString());
328 | maintainPackageManifest = null;
329 | }
330 |
331 | if (GUILayout.Button("Update Readme"))
332 | {
333 | UpdateReadme(package);
334 | }
335 |
336 | RefreshGUI();
337 | }
338 |
339 | private async void UpdateReadme(PackageInfo packageInfo)
340 | {
341 | var myPkg = await GetMyPackageInfoAsync();
342 | var readmeTemplatePath = $"{myPkg.assetPath}/Template/README_TEMPLATE.md";
343 |
344 | var readmePath = $"{packageInfo.assetPath}/README";
345 | if (!File.Exists(readmePath))
346 | {
347 | readmePath = $"{packageInfo.assetPath}/README.md";
348 | if (!File.Exists(readmePath))
349 | {
350 | Debug.LogError("Unable to find README or README.md at package asset path");
351 | return;
352 | }
353 | }
354 |
355 | var manifestPath = $"{packageInfo.assetPath}/package.json";
356 | if (!File.Exists(manifestPath))
357 | {
358 | Debug.LogError($"Unable to find package.json at {packageInfo.assetPath}");
359 | return;
360 | }
361 |
362 | PackageManifest packageManifest = null;
363 |
364 | try
365 | {
366 | packageManifest = JsonUtility.FromJson(File.ReadAllText(manifestPath));
367 | if (packageManifest == null)
368 | {
369 | Debug.LogError("Failed to read package manifest. FromJson returned null on file text.");
370 | return;
371 | }
372 | }
373 | catch (Exception e)
374 | {
375 | Debug.LogError("Failed to read package manifest format.");
376 | Debug.LogException(e);
377 | return;
378 | }
379 |
380 | if (!ValidatePackageManifest(packageManifest))
381 | {
382 | Debug.LogError("Update package manifest with required values before updating the readme");
383 | return;
384 | }
385 |
386 | var oldText = File.ReadAllText(readmePath);
387 | var templateText = File.ReadAllText(readmeTemplatePath);
388 | templateText = PackageUtility.PatchReadmeText(oldText, templateText);
389 |
390 | var readmeText = PackageUtility.CreateReadmeText(templateText, packageManifest);
391 |
392 | File.WriteAllText(readmePath,readmeText);
393 |
394 | EditorUtility.DisplayDialog("Update Readme", "Done", "OK");
395 | }
396 |
397 | private void RefreshGUI()
398 | {
399 | EditorGUILayout.BeginHorizontal();
400 | GUILayout.FlexibleSpace();
401 | if (GUILayout.Button("Refresh"))
402 | {
403 | Refresh();
404 | }
405 | EditorGUILayout.EndHorizontal();
406 | }
407 |
408 | private void CreatePackageGUI()
409 | {
410 | if (PackageGuiUtility.SourcePathGui())
411 | {
412 | Refresh();
413 | }
414 |
415 | var packageName = serializedObject.FindProperty("packageManifest.name");
416 | var packageDisplayName = serializedObject.FindProperty("packageManifest.displayName");
417 | var packageVersion = serializedObject.FindProperty("packageManifest.version");
418 | var packageDescription = serializedObject.FindProperty("packageManifest.description");
419 | var packageUnity = serializedObject.FindProperty("packageManifest.unity");
420 | var packageUnityRelease = serializedObject.FindProperty("packageManifest.unityRelease");
421 | var packageRepositoryName = serializedObject.FindProperty("packageManifest.repositoryName");
422 |
423 | var packageAuthorName = serializedObject.FindProperty("packageManifest.author.name");
424 | var packageAuthorEmail = serializedObject.FindProperty("packageManifest.author.email");
425 | var packageAuthorUrl = serializedObject.FindProperty("packageManifest.author.url");
426 | var packageAuthorGithub = serializedObject.FindProperty("packageManifest.author.twitter");
427 | var packageAuthorTwitter = serializedObject.FindProperty("packageManifest.author.github");
428 |
429 | GUILayout.BeginVertical("box");
430 |
431 | EditorGUILayout.LabelField("Package", EditorStyles.boldLabel);
432 |
433 | EditorGUI.BeginChangeCheck();
434 | EditorGUILayout.PropertyField(packageName);
435 | if (EditorGUI.EndChangeCheck())
436 | {
437 | //Enforce lowercase constraint
438 | packageName.stringValue = packageName.stringValue.ToLowerInvariant();
439 | }
440 |
441 | EditorGUILayout.PropertyField(packageRepositoryName);
442 | EditorGUILayout.PropertyField(packageDisplayName);
443 | EditorGUILayout.PropertyField(packageVersion);
444 | EditorGUILayout.PropertyField(packageDescription);
445 | EditorGUILayout.PropertyField(packageUnity);
446 | EditorGUILayout.PropertyField(packageUnityRelease);
447 |
448 | EditorGUILayout.LabelField("Author", EditorStyles.boldLabel);
449 |
450 | EditorGUILayout.PropertyField(packageAuthorName);
451 | EditorGUILayout.PropertyField(packageAuthorEmail);
452 | EditorGUILayout.PropertyField(packageAuthorUrl);
453 | EditorGUILayout.PropertyField(packageAuthorTwitter);
454 | EditorGUILayout.PropertyField(packageAuthorGithub);
455 |
456 | GUILayout.EndVertical();
457 |
458 | EditorGUILayout.Space();
459 |
460 | EditorGUILayout.BeginHorizontal();
461 | if (GUILayout.Button("Create Embeded"))
462 | {
463 | CreateEmbeded();
464 | }
465 |
466 | if (GUILayout.Button("Create at Source Path"))
467 | {
468 | CreateInSources();
469 | }
470 |
471 | EditorGUILayout.EndHorizontal();
472 |
473 | EditorGUILayout.Space();
474 | }
475 |
476 | private void CreateEmbeded()
477 | {
478 | if (ValidatePackageManifest(packageManifest))
479 | {
480 | CreateAt($"{Directory.GetCurrentDirectory()}/Packages");
481 | }
482 | }
483 |
484 | private void CreateInSources()
485 | {
486 | if (ValidatePackageManifest(packageManifest))
487 | {
488 | CreateAt(PackageSettings.SourcePath);
489 | }
490 | }
491 |
492 | private bool ValidatePackageManifest(PackageManifest manifest)
493 | {
494 | string error = null;
495 | if (string.IsNullOrEmpty(manifest.description))
496 | {
497 | error = "Package Description Required";
498 | }
499 | else if (string.IsNullOrEmpty(manifest.version))
500 | {
501 | error = "Package Version Required";
502 | }
503 | else if (string.IsNullOrEmpty(manifest.repositoryName))
504 | {
505 | error ="Package Repository Name Required";
506 | }
507 | else if (string.IsNullOrEmpty(manifest.displayName))
508 | {
509 | error = "Package display Name Required";
510 | }
511 | else if (string.IsNullOrEmpty(manifest.name))
512 | {
513 | error = "Package Name Required";
514 | }
515 | else if (string.IsNullOrEmpty(manifest.author.github))
516 | {
517 | error = "Github username required to build github links";
518 | }
519 |
520 | if (!string.IsNullOrEmpty(error))
521 | {
522 | EditorUtility.DisplayDialog("Error", error, "OK");
523 | EditorApplication.Beep();
524 | return false;
525 | }
526 |
527 | return true;
528 | }
529 |
530 | private async void CreateAt(string path)
531 | {
532 | var myPkgInfo = await GetMyPackageInfoAsync();
533 |
534 | var readmeTemplatePath = $"{myPkgInfo.assetPath}/Template/README_TEMPLATE.md";
535 | var licenseTemplatePath = $"{myPkgInfo.assetPath}/Template/Licenses/MIT LICENSE";
536 |
537 | if (!Directory.Exists(path))
538 | {
539 | EditorUtility.DisplayDialog("Error", "Unable to locate packages directory.", "OK");
540 | return;
541 | }
542 |
543 | var packagePath = $"{path}/{packageManifest.repositoryName}";
544 | if (Directory.Exists(packagePath))
545 | {
546 | EditorUtility.DisplayDialog("Error", "A Package with that name already exists.", "OK");
547 | return;
548 | }
549 |
550 | var json = EditorJsonUtility.ToJson(packageManifest, true);
551 |
552 | Directory.CreateDirectory(packagePath);
553 | Directory.CreateDirectory($"{packagePath}/Editor");
554 | Directory.CreateDirectory($"{packagePath}/Runtime");
555 | Directory.CreateDirectory($"{packagePath}/Tests/Editor");
556 | Directory.CreateDirectory($"{packagePath}/Tests/Runtime");
557 |
558 | var manifestPath = $"{packagePath}/package.json";
559 | var readmePath = $"{packagePath}/README.md";
560 | var licensePath = $"{packagePath}/LICENSE.md";
561 |
562 | var readmeText = PackageUtility.CreateReadmeText(readmeTemplatePath, packageManifest);
563 | var licenseText = PackageUtility.CreateLicenseText(File.ReadAllText(licenseTemplatePath),packageManifest);
564 |
565 | File.WriteAllText(manifestPath, json);
566 | File.WriteAllText(readmePath, readmeText);
567 | File.WriteAllText(licensePath, licenseText);
568 |
569 | var assemblyName = packageManifest.name;
570 | var editorAssemblyName = $"{packageManifest.name}.Editor";
571 | var testAssemblyName = $"{assemblyName}.Tests";
572 | var testEditorAssemblyName = $"{editorAssemblyName}.Tests";
573 |
574 | var assemblyDefPath = $"{packagePath}/Editor/{editorAssemblyName}.asmdef";
575 | var editorAssemblyDefPath = $"{packagePath}/Runtime/{assemblyName}.asmdef";
576 | var testEditorAssemblyDefPath = $"{packagePath}/Tests/Editor/{testEditorAssemblyName}.asmdef";
577 | var testAssemblyDefPath = $"{packagePath}/Tests/Runtime/{testAssemblyName}.asmdef";
578 |
579 | File.WriteAllText(assemblyDefPath,
580 | $"{{ \"name\": \"{editorAssemblyName}\", \"references\": [ \"{assemblyName}\" ], \"optionalUnityReferences\": [], \"includePlatforms\": [ \"Editor\" ], \"excludePlatforms\": [] }}");
581 | File.WriteAllText(editorAssemblyDefPath, $"{{ \"name\": \"{assemblyName}\" }}");
582 |
583 | File.WriteAllText(testEditorAssemblyDefPath,
584 | $"{{ \"name\": \"{testEditorAssemblyName}\", \"references\": [ \"{assemblyName}\" ], \"optionalUnityReferences\": [\"TestAssemblies\"], \"includePlatforms\": [ \"Editor\" ], \"excludePlatforms\": [] }}");
585 | File.WriteAllText(testAssemblyDefPath,
586 | $"{{ \"name\": \"{testAssemblyName}\", \"references\": [ \"{assemblyName}\" ], \"optionalUnityReferences\": [\"TestAssemblies\"], \"includePlatforms\": [], \"excludePlatforms\": [] }}");
587 |
588 | AssetDatabase.Refresh();
589 | EditorUtility.DisplayDialog("Package Created", "Done!", "Ok");
590 | Refresh();
591 | }
592 |
593 | }
594 | }
595 |
--------------------------------------------------------------------------------
/Editor/SimpleJson.cs:
--------------------------------------------------------------------------------
1 | /* * * * *
2 | * A simple JSON Parser / builder
3 | * ------------------------------
4 | *
5 | * It mainly has been written as a simple JSON parser. It can build a JSON string
6 | * from the node-tree, or generate a node tree from any valid JSON string.
7 | *
8 | * Written by Bunny83
9 | * 2012-06-09
10 | *
11 | * Changelog now external. See Changelog.txt
12 | *
13 | * The MIT License (MIT)
14 | *
15 | * Copyright (c) 2012-2019 Markus Göbel (Bunny83)
16 | *
17 | * Permission is hereby granted, free of charge, to any person obtaining a copy
18 | * of this software and associated documentation files (the "Software"), to deal
19 | * in the Software without restriction, including without limitation the rights
20 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21 | * copies of the Software, and to permit persons to whom the Software is
22 | * furnished to do so, subject to the following conditions:
23 | *
24 | * The above copyright notice and this permission notice shall be included in all
25 | * copies or substantial portions of the Software.
26 | *
27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33 | * SOFTWARE.
34 | *
35 | * * * * */
36 | using System;
37 | using System.Collections;
38 | using System.Collections.Generic;
39 | using System.Globalization;
40 | using System.Linq;
41 | using System.Text;
42 |
43 | namespace Gameframe.SimpleJSON
44 | {
45 | public enum JSONNodeType
46 | {
47 | Array = 1,
48 | Object = 2,
49 | String = 3,
50 | Number = 4,
51 | NullValue = 5,
52 | Boolean = 6,
53 | None = 7,
54 | Custom = 0xFF,
55 | }
56 | public enum JSONTextMode
57 | {
58 | Compact,
59 | Indent
60 | }
61 |
62 | public abstract partial class JSONNode
63 | {
64 | #region Enumerators
65 | public struct Enumerator
66 | {
67 | private enum Type { None, Array, Object }
68 | private Type type;
69 | private Dictionary.Enumerator m_Object;
70 | private List.Enumerator m_Array;
71 | public bool IsValid { get { return type != Type.None; } }
72 | public Enumerator(List.Enumerator aArrayEnum)
73 | {
74 | type = Type.Array;
75 | m_Object = default(Dictionary.Enumerator);
76 | m_Array = aArrayEnum;
77 | }
78 | public Enumerator(Dictionary.Enumerator aDictEnum)
79 | {
80 | type = Type.Object;
81 | m_Object = aDictEnum;
82 | m_Array = default(List.Enumerator);
83 | }
84 | public KeyValuePair Current
85 | {
86 | get
87 | {
88 | if (type == Type.Array)
89 | return new KeyValuePair(string.Empty, m_Array.Current);
90 | else if (type == Type.Object)
91 | return m_Object.Current;
92 | return new KeyValuePair(string.Empty, null);
93 | }
94 | }
95 | public bool MoveNext()
96 | {
97 | if (type == Type.Array)
98 | return m_Array.MoveNext();
99 | else if (type == Type.Object)
100 | return m_Object.MoveNext();
101 | return false;
102 | }
103 | }
104 | public struct ValueEnumerator
105 | {
106 | private Enumerator m_Enumerator;
107 | public ValueEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { }
108 | public ValueEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { }
109 | public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; }
110 | public JSONNode Current { get { return m_Enumerator.Current.Value; } }
111 | public bool MoveNext() { return m_Enumerator.MoveNext(); }
112 | public ValueEnumerator GetEnumerator() { return this; }
113 | }
114 | public struct KeyEnumerator
115 | {
116 | private Enumerator m_Enumerator;
117 | public KeyEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { }
118 | public KeyEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { }
119 | public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; }
120 | public string Current { get { return m_Enumerator.Current.Key; } }
121 | public bool MoveNext() { return m_Enumerator.MoveNext(); }
122 | public KeyEnumerator GetEnumerator() { return this; }
123 | }
124 |
125 | public class LinqEnumerator : IEnumerator>, IEnumerable>
126 | {
127 | private JSONNode m_Node;
128 | private Enumerator m_Enumerator;
129 | internal LinqEnumerator(JSONNode aNode)
130 | {
131 | m_Node = aNode;
132 | if (m_Node != null)
133 | m_Enumerator = m_Node.GetEnumerator();
134 | }
135 | public KeyValuePair Current { get { return m_Enumerator.Current; } }
136 | object IEnumerator.Current { get { return m_Enumerator.Current; } }
137 | public bool MoveNext() { return m_Enumerator.MoveNext(); }
138 |
139 | public void Dispose()
140 | {
141 | m_Node = null;
142 | m_Enumerator = new Enumerator();
143 | }
144 |
145 | public IEnumerator> GetEnumerator()
146 | {
147 | return new LinqEnumerator(m_Node);
148 | }
149 |
150 | public void Reset()
151 | {
152 | if (m_Node != null)
153 | m_Enumerator = m_Node.GetEnumerator();
154 | }
155 |
156 | IEnumerator IEnumerable.GetEnumerator()
157 | {
158 | return new LinqEnumerator(m_Node);
159 | }
160 | }
161 |
162 | #endregion Enumerators
163 |
164 | #region common interface
165 |
166 | public static bool forceASCII = false; // Use Unicode by default
167 | public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber
168 | public static bool allowLineComments = true; // allow "//"-style comments at the end of a line
169 |
170 | public abstract JSONNodeType Tag { get; }
171 |
172 | public virtual JSONNode this[int aIndex] { get { return null; } set { } }
173 |
174 | public virtual JSONNode this[string aKey] { get { return null; } set { } }
175 |
176 | public virtual string Value { get { return ""; } set { } }
177 |
178 | public virtual int Count { get { return 0; } }
179 |
180 | public virtual bool IsNumber { get { return false; } }
181 | public virtual bool IsString { get { return false; } }
182 | public virtual bool IsBoolean { get { return false; } }
183 | public virtual bool IsNull { get { return false; } }
184 | public virtual bool IsArray { get { return false; } }
185 | public virtual bool IsObject { get { return false; } }
186 |
187 | public virtual bool Inline { get { return false; } set { } }
188 |
189 | public virtual void Add(string aKey, JSONNode aItem)
190 | {
191 | }
192 | public virtual void Add(JSONNode aItem)
193 | {
194 | Add("", aItem);
195 | }
196 |
197 | public virtual JSONNode Remove(string aKey)
198 | {
199 | return null;
200 | }
201 |
202 | public virtual JSONNode Remove(int aIndex)
203 | {
204 | return null;
205 | }
206 |
207 | public virtual JSONNode Remove(JSONNode aNode)
208 | {
209 | return aNode;
210 | }
211 |
212 | public virtual JSONNode Clone()
213 | {
214 | return null;
215 | }
216 |
217 | public virtual IEnumerable Children
218 | {
219 | get
220 | {
221 | yield break;
222 | }
223 | }
224 |
225 | public IEnumerable DeepChildren
226 | {
227 | get
228 | {
229 | foreach (var C in Children)
230 | foreach (var D in C.DeepChildren)
231 | yield return D;
232 | }
233 | }
234 |
235 | public virtual bool HasKey(string aKey)
236 | {
237 | return false;
238 | }
239 |
240 | public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault)
241 | {
242 | return aDefault;
243 | }
244 |
245 | public override string ToString()
246 | {
247 | StringBuilder sb = new StringBuilder();
248 | WriteToStringBuilder(sb, 0, 0, JSONTextMode.Compact);
249 | return sb.ToString();
250 | }
251 |
252 | public virtual string ToString(int aIndent)
253 | {
254 | StringBuilder sb = new StringBuilder();
255 | WriteToStringBuilder(sb, 0, aIndent, JSONTextMode.Indent);
256 | return sb.ToString();
257 | }
258 | internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode);
259 |
260 | public abstract Enumerator GetEnumerator();
261 | public IEnumerable> Linq { get { return new LinqEnumerator(this); } }
262 | public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } }
263 | public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } }
264 |
265 | #endregion common interface
266 |
267 | #region typecasting properties
268 |
269 |
270 | public virtual double AsDouble
271 | {
272 | get
273 | {
274 | double v = 0.0;
275 | if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v))
276 | return v;
277 | return 0.0;
278 | }
279 | set
280 | {
281 | Value = value.ToString(CultureInfo.InvariantCulture);
282 | }
283 | }
284 |
285 | public virtual int AsInt
286 | {
287 | get { return (int)AsDouble; }
288 | set { AsDouble = value; }
289 | }
290 |
291 | public virtual float AsFloat
292 | {
293 | get { return (float)AsDouble; }
294 | set { AsDouble = value; }
295 | }
296 |
297 | public virtual bool AsBool
298 | {
299 | get
300 | {
301 | bool v = false;
302 | if (bool.TryParse(Value, out v))
303 | return v;
304 | return !string.IsNullOrEmpty(Value);
305 | }
306 | set
307 | {
308 | Value = (value) ? "true" : "false";
309 | }
310 | }
311 |
312 | public virtual long AsLong
313 | {
314 | get
315 | {
316 | long val = 0;
317 | if (long.TryParse(Value, out val))
318 | return val;
319 | return 0L;
320 | }
321 | set
322 | {
323 | Value = value.ToString();
324 | }
325 | }
326 |
327 | public virtual JSONArray AsArray
328 | {
329 | get
330 | {
331 | return this as JSONArray;
332 | }
333 | }
334 |
335 | public virtual JSONObject AsObject
336 | {
337 | get
338 | {
339 | return this as JSONObject;
340 | }
341 | }
342 |
343 |
344 | #endregion typecasting properties
345 |
346 | #region operators
347 |
348 | public static implicit operator JSONNode(string s)
349 | {
350 | return new JSONString(s);
351 | }
352 | public static implicit operator string(JSONNode d)
353 | {
354 | return (d == null) ? null : d.Value;
355 | }
356 |
357 | public static implicit operator JSONNode(double n)
358 | {
359 | return new JSONNumber(n);
360 | }
361 | public static implicit operator double(JSONNode d)
362 | {
363 | return (d == null) ? 0 : d.AsDouble;
364 | }
365 |
366 | public static implicit operator JSONNode(float n)
367 | {
368 | return new JSONNumber(n);
369 | }
370 | public static implicit operator float(JSONNode d)
371 | {
372 | return (d == null) ? 0 : d.AsFloat;
373 | }
374 |
375 | public static implicit operator JSONNode(int n)
376 | {
377 | return new JSONNumber(n);
378 | }
379 | public static implicit operator int(JSONNode d)
380 | {
381 | return (d == null) ? 0 : d.AsInt;
382 | }
383 |
384 | public static implicit operator JSONNode(long n)
385 | {
386 | if (longAsString)
387 | return new JSONString(n.ToString());
388 | return new JSONNumber(n);
389 | }
390 | public static implicit operator long(JSONNode d)
391 | {
392 | return (d == null) ? 0L : d.AsLong;
393 | }
394 |
395 | public static implicit operator JSONNode(bool b)
396 | {
397 | return new JSONBool(b);
398 | }
399 | public static implicit operator bool(JSONNode d)
400 | {
401 | return (d == null) ? false : d.AsBool;
402 | }
403 |
404 | public static implicit operator JSONNode(KeyValuePair aKeyValue)
405 | {
406 | return aKeyValue.Value;
407 | }
408 |
409 | public static bool operator ==(JSONNode a, object b)
410 | {
411 | if (ReferenceEquals(a, b))
412 | return true;
413 | bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator;
414 | bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator;
415 | if (aIsNull && bIsNull)
416 | return true;
417 | return !aIsNull && a.Equals(b);
418 | }
419 |
420 | public static bool operator !=(JSONNode a, object b)
421 | {
422 | return !(a == b);
423 | }
424 |
425 | public override bool Equals(object obj)
426 | {
427 | return ReferenceEquals(this, obj);
428 | }
429 |
430 | public override int GetHashCode()
431 | {
432 | return base.GetHashCode();
433 | }
434 |
435 | #endregion operators
436 |
437 | [ThreadStatic]
438 | private static StringBuilder m_EscapeBuilder;
439 | internal static StringBuilder EscapeBuilder
440 | {
441 | get
442 | {
443 | if (m_EscapeBuilder == null)
444 | m_EscapeBuilder = new StringBuilder();
445 | return m_EscapeBuilder;
446 | }
447 | }
448 | internal static string Escape(string aText)
449 | {
450 | var sb = EscapeBuilder;
451 | sb.Length = 0;
452 | if (sb.Capacity < aText.Length + aText.Length / 10)
453 | sb.Capacity = aText.Length + aText.Length / 10;
454 | foreach (char c in aText)
455 | {
456 | switch (c)
457 | {
458 | case '\\':
459 | sb.Append("\\\\");
460 | break;
461 | case '\"':
462 | sb.Append("\\\"");
463 | break;
464 | case '\n':
465 | sb.Append("\\n");
466 | break;
467 | case '\r':
468 | sb.Append("\\r");
469 | break;
470 | case '\t':
471 | sb.Append("\\t");
472 | break;
473 | case '\b':
474 | sb.Append("\\b");
475 | break;
476 | case '\f':
477 | sb.Append("\\f");
478 | break;
479 | default:
480 | if (c < ' ' || (forceASCII && c > 127))
481 | {
482 | ushort val = c;
483 | sb.Append("\\u").Append(val.ToString("X4"));
484 | }
485 | else
486 | sb.Append(c);
487 | break;
488 | }
489 | }
490 | string result = sb.ToString();
491 | sb.Length = 0;
492 | return result;
493 | }
494 |
495 | private static JSONNode ParseElement(string token, bool quoted)
496 | {
497 | if (quoted)
498 | return token;
499 | string tmp = token.ToLower();
500 | if (tmp == "false" || tmp == "true")
501 | return tmp == "true";
502 | if (tmp == "null")
503 | return JSONNull.CreateOrGet();
504 | double val;
505 | if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out val))
506 | return val;
507 | else
508 | return token;
509 | }
510 |
511 | public static JSONNode Parse(string aJSON)
512 | {
513 | Stack stack = new Stack();
514 | JSONNode ctx = null;
515 | int i = 0;
516 | StringBuilder Token = new StringBuilder();
517 | string TokenName = "";
518 | bool QuoteMode = false;
519 | bool TokenIsQuoted = false;
520 | while (i < aJSON.Length)
521 | {
522 | switch (aJSON[i])
523 | {
524 | case '{':
525 | if (QuoteMode)
526 | {
527 | Token.Append(aJSON[i]);
528 | break;
529 | }
530 | stack.Push(new JSONObject());
531 | if (ctx != null)
532 | {
533 | ctx.Add(TokenName, stack.Peek());
534 | }
535 | TokenName = "";
536 | Token.Length = 0;
537 | ctx = stack.Peek();
538 | break;
539 |
540 | case '[':
541 | if (QuoteMode)
542 | {
543 | Token.Append(aJSON[i]);
544 | break;
545 | }
546 |
547 | stack.Push(new JSONArray());
548 | if (ctx != null)
549 | {
550 | ctx.Add(TokenName, stack.Peek());
551 | }
552 | TokenName = "";
553 | Token.Length = 0;
554 | ctx = stack.Peek();
555 | break;
556 |
557 | case '}':
558 | case ']':
559 | if (QuoteMode)
560 | {
561 |
562 | Token.Append(aJSON[i]);
563 | break;
564 | }
565 | if (stack.Count == 0)
566 | throw new Exception("JSON Parse: Too many closing brackets");
567 |
568 | stack.Pop();
569 | if (Token.Length > 0 || TokenIsQuoted)
570 | ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted));
571 | TokenIsQuoted = false;
572 | TokenName = "";
573 | Token.Length = 0;
574 | if (stack.Count > 0)
575 | ctx = stack.Peek();
576 | break;
577 |
578 | case ':':
579 | if (QuoteMode)
580 | {
581 | Token.Append(aJSON[i]);
582 | break;
583 | }
584 | TokenName = Token.ToString();
585 | Token.Length = 0;
586 | TokenIsQuoted = false;
587 | break;
588 |
589 | case '"':
590 | QuoteMode ^= true;
591 | TokenIsQuoted |= QuoteMode;
592 | break;
593 |
594 | case ',':
595 | if (QuoteMode)
596 | {
597 | Token.Append(aJSON[i]);
598 | break;
599 | }
600 | if (Token.Length > 0 || TokenIsQuoted)
601 | ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted));
602 | TokenIsQuoted = false;
603 | TokenName = "";
604 | Token.Length = 0;
605 | TokenIsQuoted = false;
606 | break;
607 |
608 | case '\r':
609 | case '\n':
610 | break;
611 |
612 | case ' ':
613 | case '\t':
614 | if (QuoteMode)
615 | Token.Append(aJSON[i]);
616 | break;
617 |
618 | case '\\':
619 | ++i;
620 | if (QuoteMode)
621 | {
622 | char C = aJSON[i];
623 | switch (C)
624 | {
625 | case 't':
626 | Token.Append('\t');
627 | break;
628 | case 'r':
629 | Token.Append('\r');
630 | break;
631 | case 'n':
632 | Token.Append('\n');
633 | break;
634 | case 'b':
635 | Token.Append('\b');
636 | break;
637 | case 'f':
638 | Token.Append('\f');
639 | break;
640 | case 'u':
641 | {
642 | string s = aJSON.Substring(i + 1, 4);
643 | Token.Append((char)int.Parse(
644 | s,
645 | System.Globalization.NumberStyles.AllowHexSpecifier));
646 | i += 4;
647 | break;
648 | }
649 | default:
650 | Token.Append(C);
651 | break;
652 | }
653 | }
654 | break;
655 | case '/':
656 | if (allowLineComments && !QuoteMode && i + 1 < aJSON.Length && aJSON[i + 1] == '/')
657 | {
658 | while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') ;
659 | break;
660 | }
661 | Token.Append(aJSON[i]);
662 | break;
663 | case '\uFEFF': // remove / ignore BOM (Byte Order Mark)
664 | break;
665 |
666 | default:
667 | Token.Append(aJSON[i]);
668 | break;
669 | }
670 | ++i;
671 | }
672 | if (QuoteMode)
673 | {
674 | throw new Exception("JSON Parse: Quotation marks seems to be messed up.");
675 | }
676 | if (ctx == null)
677 | return ParseElement(Token.ToString(), TokenIsQuoted);
678 | return ctx;
679 | }
680 |
681 | }
682 | // End of JSONNode
683 |
684 | public partial class JSONArray : JSONNode
685 | {
686 | private List m_List = new List();
687 | private bool inline = false;
688 | public override bool Inline
689 | {
690 | get { return inline; }
691 | set { inline = value; }
692 | }
693 |
694 | public override JSONNodeType Tag { get { return JSONNodeType.Array; } }
695 | public override bool IsArray { get { return true; } }
696 | public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); }
697 |
698 | public override JSONNode this[int aIndex]
699 | {
700 | get
701 | {
702 | if (aIndex < 0 || aIndex >= m_List.Count)
703 | return new JSONLazyCreator(this);
704 | return m_List[aIndex];
705 | }
706 | set
707 | {
708 | if (value == null)
709 | value = JSONNull.CreateOrGet();
710 | if (aIndex < 0 || aIndex >= m_List.Count)
711 | m_List.Add(value);
712 | else
713 | m_List[aIndex] = value;
714 | }
715 | }
716 |
717 | public override JSONNode this[string aKey]
718 | {
719 | get { return new JSONLazyCreator(this); }
720 | set
721 | {
722 | if (value == null)
723 | value = JSONNull.CreateOrGet();
724 | m_List.Add(value);
725 | }
726 | }
727 |
728 | public override int Count
729 | {
730 | get { return m_List.Count; }
731 | }
732 |
733 | public override void Add(string aKey, JSONNode aItem)
734 | {
735 | if (aItem == null)
736 | aItem = JSONNull.CreateOrGet();
737 | m_List.Add(aItem);
738 | }
739 |
740 | public override JSONNode Remove(int aIndex)
741 | {
742 | if (aIndex < 0 || aIndex >= m_List.Count)
743 | return null;
744 | JSONNode tmp = m_List[aIndex];
745 | m_List.RemoveAt(aIndex);
746 | return tmp;
747 | }
748 |
749 | public override JSONNode Remove(JSONNode aNode)
750 | {
751 | m_List.Remove(aNode);
752 | return aNode;
753 | }
754 |
755 | public override JSONNode Clone()
756 | {
757 | var node = new JSONArray();
758 | node.m_List.Capacity = m_List.Capacity;
759 | foreach(var n in m_List)
760 | {
761 | if (n != null)
762 | node.Add(n.Clone());
763 | else
764 | node.Add(null);
765 | }
766 | return node;
767 | }
768 |
769 | public override IEnumerable Children
770 | {
771 | get
772 | {
773 | foreach (JSONNode N in m_List)
774 | yield return N;
775 | }
776 | }
777 |
778 |
779 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
780 | {
781 | aSB.Append('[');
782 | int count = m_List.Count;
783 | if (inline)
784 | aMode = JSONTextMode.Compact;
785 | for (int i = 0; i < count; i++)
786 | {
787 | if (i > 0)
788 | aSB.Append(',');
789 | if (aMode == JSONTextMode.Indent)
790 | aSB.AppendLine();
791 |
792 | if (aMode == JSONTextMode.Indent)
793 | aSB.Append(' ', aIndent + aIndentInc);
794 | m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode);
795 | }
796 | if (aMode == JSONTextMode.Indent)
797 | aSB.AppendLine().Append(' ', aIndent);
798 | aSB.Append(']');
799 | }
800 | }
801 | // End of JSONArray
802 |
803 | public partial class JSONObject : JSONNode
804 | {
805 | private Dictionary m_Dict = new Dictionary();
806 |
807 | private bool inline = false;
808 | public override bool Inline
809 | {
810 | get { return inline; }
811 | set { inline = value; }
812 | }
813 |
814 | public override JSONNodeType Tag { get { return JSONNodeType.Object; } }
815 | public override bool IsObject { get { return true; } }
816 |
817 | public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); }
818 |
819 |
820 | public override JSONNode this[string aKey]
821 | {
822 | get
823 | {
824 | if (m_Dict.ContainsKey(aKey))
825 | return m_Dict[aKey];
826 | else
827 | return new JSONLazyCreator(this, aKey);
828 | }
829 | set
830 | {
831 | if (value == null)
832 | value = JSONNull.CreateOrGet();
833 | if (m_Dict.ContainsKey(aKey))
834 | m_Dict[aKey] = value;
835 | else
836 | m_Dict.Add(aKey, value);
837 | }
838 | }
839 |
840 | public override JSONNode this[int aIndex]
841 | {
842 | get
843 | {
844 | if (aIndex < 0 || aIndex >= m_Dict.Count)
845 | return null;
846 | return m_Dict.ElementAt(aIndex).Value;
847 | }
848 | set
849 | {
850 | if (value == null)
851 | value = JSONNull.CreateOrGet();
852 | if (aIndex < 0 || aIndex >= m_Dict.Count)
853 | return;
854 | string key = m_Dict.ElementAt(aIndex).Key;
855 | m_Dict[key] = value;
856 | }
857 | }
858 |
859 | public override int Count
860 | {
861 | get { return m_Dict.Count; }
862 | }
863 |
864 | public override void Add(string aKey, JSONNode aItem)
865 | {
866 | if (aItem == null)
867 | aItem = JSONNull.CreateOrGet();
868 |
869 | if (aKey != null)
870 | {
871 | if (m_Dict.ContainsKey(aKey))
872 | m_Dict[aKey] = aItem;
873 | else
874 | m_Dict.Add(aKey, aItem);
875 | }
876 | else
877 | m_Dict.Add(Guid.NewGuid().ToString(), aItem);
878 | }
879 |
880 | public override JSONNode Remove(string aKey)
881 | {
882 | if (!m_Dict.ContainsKey(aKey))
883 | return null;
884 | JSONNode tmp = m_Dict[aKey];
885 | m_Dict.Remove(aKey);
886 | return tmp;
887 | }
888 |
889 | public override JSONNode Remove(int aIndex)
890 | {
891 | if (aIndex < 0 || aIndex >= m_Dict.Count)
892 | return null;
893 | var item = m_Dict.ElementAt(aIndex);
894 | m_Dict.Remove(item.Key);
895 | return item.Value;
896 | }
897 |
898 | public override JSONNode Remove(JSONNode aNode)
899 | {
900 | try
901 | {
902 | var item = m_Dict.Where(k => k.Value == aNode).First();
903 | m_Dict.Remove(item.Key);
904 | return aNode;
905 | }
906 | catch
907 | {
908 | return null;
909 | }
910 | }
911 |
912 | public override JSONNode Clone()
913 | {
914 | var node = new JSONObject();
915 | foreach (var n in m_Dict)
916 | {
917 | node.Add(n.Key, n.Value.Clone());
918 | }
919 | return node;
920 | }
921 |
922 | public override bool HasKey(string aKey)
923 | {
924 | return m_Dict.ContainsKey(aKey);
925 | }
926 |
927 | public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault)
928 | {
929 | JSONNode res;
930 | if (m_Dict.TryGetValue(aKey, out res))
931 | return res;
932 | return aDefault;
933 | }
934 |
935 | public override IEnumerable Children
936 | {
937 | get
938 | {
939 | foreach (KeyValuePair N in m_Dict)
940 | yield return N.Value;
941 | }
942 | }
943 |
944 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
945 | {
946 | aSB.Append('{');
947 | bool first = true;
948 | if (inline)
949 | aMode = JSONTextMode.Compact;
950 | foreach (var k in m_Dict)
951 | {
952 | if (!first)
953 | aSB.Append(',');
954 | first = false;
955 | if (aMode == JSONTextMode.Indent)
956 | aSB.AppendLine();
957 | if (aMode == JSONTextMode.Indent)
958 | aSB.Append(' ', aIndent + aIndentInc);
959 | aSB.Append('\"').Append(Escape(k.Key)).Append('\"');
960 | if (aMode == JSONTextMode.Compact)
961 | aSB.Append(':');
962 | else
963 | aSB.Append(" : ");
964 | k.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode);
965 | }
966 | if (aMode == JSONTextMode.Indent)
967 | aSB.AppendLine().Append(' ', aIndent);
968 | aSB.Append('}');
969 | }
970 |
971 | }
972 | // End of JSONObject
973 |
974 | public partial class JSONString : JSONNode
975 | {
976 | private string m_Data;
977 |
978 | public override JSONNodeType Tag { get { return JSONNodeType.String; } }
979 | public override bool IsString { get { return true; } }
980 |
981 | public override Enumerator GetEnumerator() { return new Enumerator(); }
982 |
983 |
984 | public override string Value
985 | {
986 | get { return m_Data; }
987 | set
988 | {
989 | m_Data = value;
990 | }
991 | }
992 |
993 | public JSONString(string aData)
994 | {
995 | m_Data = aData;
996 | }
997 | public override JSONNode Clone()
998 | {
999 | return new JSONString(m_Data);
1000 | }
1001 |
1002 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
1003 | {
1004 | aSB.Append('\"').Append(Escape(m_Data)).Append('\"');
1005 | }
1006 | public override bool Equals(object obj)
1007 | {
1008 | if (base.Equals(obj))
1009 | return true;
1010 | string s = obj as string;
1011 | if (s != null)
1012 | return m_Data == s;
1013 | JSONString s2 = obj as JSONString;
1014 | if (s2 != null)
1015 | return m_Data == s2.m_Data;
1016 | return false;
1017 | }
1018 | public override int GetHashCode()
1019 | {
1020 | return m_Data.GetHashCode();
1021 | }
1022 | }
1023 | // End of JSONString
1024 |
1025 | public partial class JSONNumber : JSONNode
1026 | {
1027 | private double m_Data;
1028 |
1029 | public override JSONNodeType Tag { get { return JSONNodeType.Number; } }
1030 | public override bool IsNumber { get { return true; } }
1031 | public override Enumerator GetEnumerator() { return new Enumerator(); }
1032 |
1033 | public override string Value
1034 | {
1035 | get { return m_Data.ToString(CultureInfo.InvariantCulture); }
1036 | set
1037 | {
1038 | double v;
1039 | if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out v))
1040 | m_Data = v;
1041 | }
1042 | }
1043 |
1044 | public override double AsDouble
1045 | {
1046 | get { return m_Data; }
1047 | set { m_Data = value; }
1048 | }
1049 | public override long AsLong
1050 | {
1051 | get { return (long)m_Data; }
1052 | set { m_Data = value; }
1053 | }
1054 |
1055 | public JSONNumber(double aData)
1056 | {
1057 | m_Data = aData;
1058 | }
1059 |
1060 | public JSONNumber(string aData)
1061 | {
1062 | Value = aData;
1063 | }
1064 |
1065 | public override JSONNode Clone()
1066 | {
1067 | return new JSONNumber(m_Data);
1068 | }
1069 |
1070 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
1071 | {
1072 | aSB.Append(Value);
1073 | }
1074 | private static bool IsNumeric(object value)
1075 | {
1076 | return value is int || value is uint
1077 | || value is float || value is double
1078 | || value is decimal
1079 | || value is long || value is ulong
1080 | || value is short || value is ushort
1081 | || value is sbyte || value is byte;
1082 | }
1083 | public override bool Equals(object obj)
1084 | {
1085 | if (obj == null)
1086 | return false;
1087 | if (base.Equals(obj))
1088 | return true;
1089 | JSONNumber s2 = obj as JSONNumber;
1090 | if (s2 != null)
1091 | return m_Data == s2.m_Data;
1092 | if (IsNumeric(obj))
1093 | return Convert.ToDouble(obj) == m_Data;
1094 | return false;
1095 | }
1096 | public override int GetHashCode()
1097 | {
1098 | return m_Data.GetHashCode();
1099 | }
1100 | }
1101 | // End of JSONNumber
1102 |
1103 | public partial class JSONBool : JSONNode
1104 | {
1105 | private bool m_Data;
1106 |
1107 | public override JSONNodeType Tag { get { return JSONNodeType.Boolean; } }
1108 | public override bool IsBoolean { get { return true; } }
1109 | public override Enumerator GetEnumerator() { return new Enumerator(); }
1110 |
1111 | public override string Value
1112 | {
1113 | get { return m_Data.ToString(); }
1114 | set
1115 | {
1116 | bool v;
1117 | if (bool.TryParse(value, out v))
1118 | m_Data = v;
1119 | }
1120 | }
1121 | public override bool AsBool
1122 | {
1123 | get { return m_Data; }
1124 | set { m_Data = value; }
1125 | }
1126 |
1127 | public JSONBool(bool aData)
1128 | {
1129 | m_Data = aData;
1130 | }
1131 |
1132 | public JSONBool(string aData)
1133 | {
1134 | Value = aData;
1135 | }
1136 |
1137 | public override JSONNode Clone()
1138 | {
1139 | return new JSONBool(m_Data);
1140 | }
1141 |
1142 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
1143 | {
1144 | aSB.Append((m_Data) ? "true" : "false");
1145 | }
1146 | public override bool Equals(object obj)
1147 | {
1148 | if (obj == null)
1149 | return false;
1150 | if (obj is bool)
1151 | return m_Data == (bool)obj;
1152 | return false;
1153 | }
1154 | public override int GetHashCode()
1155 | {
1156 | return m_Data.GetHashCode();
1157 | }
1158 | }
1159 | // End of JSONBool
1160 |
1161 | public partial class JSONNull : JSONNode
1162 | {
1163 | static JSONNull m_StaticInstance = new JSONNull();
1164 | public static bool reuseSameInstance = true;
1165 | public static JSONNull CreateOrGet()
1166 | {
1167 | if (reuseSameInstance)
1168 | return m_StaticInstance;
1169 | return new JSONNull();
1170 | }
1171 | private JSONNull() { }
1172 |
1173 | public override JSONNodeType Tag { get { return JSONNodeType.NullValue; } }
1174 | public override bool IsNull { get { return true; } }
1175 | public override Enumerator GetEnumerator() { return new Enumerator(); }
1176 |
1177 | public override string Value
1178 | {
1179 | get { return "null"; }
1180 | set { }
1181 | }
1182 | public override bool AsBool
1183 | {
1184 | get { return false; }
1185 | set { }
1186 | }
1187 |
1188 | public override JSONNode Clone()
1189 | {
1190 | return CreateOrGet();
1191 | }
1192 |
1193 | public override bool Equals(object obj)
1194 | {
1195 | if (object.ReferenceEquals(this, obj))
1196 | return true;
1197 | return (obj is JSONNull);
1198 | }
1199 | public override int GetHashCode()
1200 | {
1201 | return 0;
1202 | }
1203 |
1204 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
1205 | {
1206 | aSB.Append("null");
1207 | }
1208 | }
1209 | // End of JSONNull
1210 |
1211 | internal partial class JSONLazyCreator : JSONNode
1212 | {
1213 | private JSONNode m_Node = null;
1214 | private string m_Key = null;
1215 | public override JSONNodeType Tag { get { return JSONNodeType.None; } }
1216 | public override Enumerator GetEnumerator() { return new Enumerator(); }
1217 |
1218 | public JSONLazyCreator(JSONNode aNode)
1219 | {
1220 | m_Node = aNode;
1221 | m_Key = null;
1222 | }
1223 |
1224 | public JSONLazyCreator(JSONNode aNode, string aKey)
1225 | {
1226 | m_Node = aNode;
1227 | m_Key = aKey;
1228 | }
1229 |
1230 | private T Set(T aVal) where T : JSONNode
1231 | {
1232 | if (m_Key == null)
1233 | m_Node.Add(aVal);
1234 | else
1235 | m_Node.Add(m_Key, aVal);
1236 | m_Node = null; // Be GC friendly.
1237 | return aVal;
1238 | }
1239 |
1240 | public override JSONNode this[int aIndex]
1241 | {
1242 | get { return new JSONLazyCreator(this); }
1243 | set { Set(new JSONArray()).Add(value); }
1244 | }
1245 |
1246 | public override JSONNode this[string aKey]
1247 | {
1248 | get { return new JSONLazyCreator(this, aKey); }
1249 | set { Set(new JSONObject()).Add(aKey, value); }
1250 | }
1251 |
1252 | public override void Add(JSONNode aItem)
1253 | {
1254 | Set(new JSONArray()).Add(aItem);
1255 | }
1256 |
1257 | public override void Add(string aKey, JSONNode aItem)
1258 | {
1259 | Set(new JSONObject()).Add(aKey, aItem);
1260 | }
1261 |
1262 | public static bool operator ==(JSONLazyCreator a, object b)
1263 | {
1264 | if (b == null)
1265 | return true;
1266 | return System.Object.ReferenceEquals(a, b);
1267 | }
1268 |
1269 | public static bool operator !=(JSONLazyCreator a, object b)
1270 | {
1271 | return !(a == b);
1272 | }
1273 |
1274 | public override bool Equals(object obj)
1275 | {
1276 | if (obj == null)
1277 | return true;
1278 | return System.Object.ReferenceEquals(this, obj);
1279 | }
1280 |
1281 | public override int GetHashCode()
1282 | {
1283 | return 0;
1284 | }
1285 |
1286 | public override int AsInt
1287 | {
1288 | get { Set(new JSONNumber(0)); return 0; }
1289 | set { Set(new JSONNumber(value)); }
1290 | }
1291 |
1292 | public override float AsFloat
1293 | {
1294 | get { Set(new JSONNumber(0.0f)); return 0.0f; }
1295 | set { Set(new JSONNumber(value)); }
1296 | }
1297 |
1298 | public override double AsDouble
1299 | {
1300 | get { Set(new JSONNumber(0.0)); return 0.0; }
1301 | set { Set(new JSONNumber(value)); }
1302 | }
1303 |
1304 | public override long AsLong
1305 | {
1306 | get
1307 | {
1308 | if (longAsString)
1309 | Set(new JSONString("0"));
1310 | else
1311 | Set(new JSONNumber(0.0));
1312 | return 0L;
1313 | }
1314 | set
1315 | {
1316 | if (longAsString)
1317 | Set(new JSONString(value.ToString()));
1318 | else
1319 | Set(new JSONNumber(value));
1320 | }
1321 | }
1322 |
1323 | public override bool AsBool
1324 | {
1325 | get { Set(new JSONBool(false)); return false; }
1326 | set { Set(new JSONBool(value)); }
1327 | }
1328 |
1329 | public override JSONArray AsArray
1330 | {
1331 | get { return Set(new JSONArray()); }
1332 | }
1333 |
1334 | public override JSONObject AsObject
1335 | {
1336 | get { return Set(new JSONObject()); }
1337 | }
1338 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
1339 | {
1340 | aSB.Append("null");
1341 | }
1342 | }
1343 | // End of JSONLazyCreator
1344 |
1345 | public static class JSON
1346 | {
1347 | public static JSONNode Parse(string aJSON)
1348 | {
1349 | return JSONNode.Parse(aJSON);
1350 | }
1351 | }
1352 | }
--------------------------------------------------------------------------------