gg.easy.airship 0.1.2107 → 0.1.2108

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,30 @@ namespace Editor.Accessories.Clothing {
26
26
  private static string easyOrgId = "6b62d6e3-9d74-449c-aeac-b4feed2012b1";
27
27
  private bool skipBuild = false;
28
28
 
29
+ [MenuItem("Airship/Internal/Publish All Platform Gear")]
30
+ public static async void PublishAllPlatformGearBundles() {
31
+ List<PlatformGearBundleManifest> gearBundles = new();
32
+
33
+ // Get all asset GUIDs
34
+ string[] guids = AssetDatabase.FindAssets("t:PlatformGearBundleManifest");
35
+ foreach (string guid in guids) {
36
+ string path = AssetDatabase.GUIDToAssetPath(guid);
37
+ var obj = AssetDatabase.LoadAssetAtPath<PlatformGearBundleManifest>(path);
38
+ if (obj != null) {
39
+ gearBundles.Add(obj);
40
+ }
41
+ }
42
+
43
+ int counter = 0;
44
+ foreach (var gearBundle in gearBundles) {
45
+ var editor = CreateEditor(gearBundle) as PlatformGearBundleManifestEditor;
46
+ await editor.BuildAllPlatforms();
47
+ counter++;
48
+ Debug.Log($"Gear publish progress: {counter}/{guids.Length}");
49
+ await Awaitable.WaitForSecondsAsync(0.1f);
50
+ }
51
+ }
52
+
29
53
  private void OnEnable() {
30
54
  skipBuild = false;
31
55
  }
@@ -45,7 +69,7 @@ namespace Editor.Accessories.Clothing {
45
69
  this.skipBuild = EditorGUILayout.Toggle("Skip Build", this.skipBuild);
46
70
  }
47
71
 
48
- private async void BuildAllPlatforms() {
72
+ public async Task BuildAllPlatforms() {
49
73
  var st = Stopwatch.StartNew();
50
74
  bool success = true;
51
75
 
@@ -41,6 +41,8 @@ namespace Editor {
41
41
 
42
42
  private static void OnBuild() {
43
43
  PhysicsSetup.Setup(null);
44
+
45
+
44
46
  }
45
47
 
46
48
  public static void BuildLinuxServerStaging() {
@@ -10,6 +10,7 @@ using UnityEditor.Build.Pipeline;
10
10
  using UnityEditor.Build.Pipeline.Interfaces;
11
11
  using UnityEditor.Build.Pipeline.Tasks;
12
12
  using UnityEngine;
13
+ using UnityEngine.Rendering;
13
14
  #if UNITY_EDITOR
14
15
  using UnityEditor;
15
16
  #endif
@@ -356,8 +357,18 @@ public static class CreateAssetBundles {
356
357
  });
357
358
  }
358
359
  }
360
+
359
361
  // var tasks = DefaultBuildTasks.Create(DefaultBuildTasks.Preset.AssetBundleBuiltInShaderExtraction);
360
362
  var buildTarget = AirshipPlatformUtil.ToBuildTarget(platform);
363
+
364
+ if (platform == AirshipPlatform.Android) {
365
+ PlayerSettings.SetUseDefaultGraphicsAPIs(buildTarget, false);
366
+ PlayerSettings.SetGraphicsAPIs(buildTarget, new GraphicsDeviceType[]
367
+ {
368
+ GraphicsDeviceType.OpenGLES3
369
+ });
370
+ }
371
+
361
372
  var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget);
362
373
  if (platform is AirshipPlatform.Windows or AirshipPlatform.Mac or AirshipPlatform.Linux) {
363
374
  buildTargetGroup = BuildTargetGroup.Standalone;
@@ -24,6 +24,7 @@ using UnityEditor;
24
24
  using UnityEditor.Build.Pipeline;
25
25
  using UnityEngine;
26
26
  using UnityEngine.Networking;
27
+ using UnityEngine.Rendering;
27
28
  using Debug = UnityEngine.Debug;
28
29
  using Object = UnityEngine.Object;
29
30
  using ZipFile = Unity.VisualScripting.IonicZip.ZipFile;
@@ -423,6 +424,13 @@ namespace Editor.Packages {
423
424
  if (platform is AirshipPlatform.Windows or AirshipPlatform.Mac or AirshipPlatform.Linux) {
424
425
  buildTargetGroup = BuildTargetGroup.Standalone;
425
426
  }
427
+ if (platform == AirshipPlatform.Android) {
428
+ PlayerSettings.SetUseDefaultGraphicsAPIs(buildTarget, false);
429
+ PlayerSettings.SetGraphicsAPIs(buildTarget, new GraphicsDeviceType[]
430
+ {
431
+ GraphicsDeviceType.OpenGLES3
432
+ });
433
+ }
426
434
 
427
435
  var buildParams = new BundleBuildParameters(
428
436
  buildTarget,
@@ -8,6 +8,7 @@ public class CreateGameDeploymentDto {
8
8
  public bool deployCode;
9
9
  public bool deployAssets;
10
10
  public string[] packageSlugs;
11
+ public string[] platforms;
11
12
  }
12
13
 
13
14
  [Serializable]
@@ -150,6 +150,13 @@ public class Deploy {
150
150
  DeploymentDto deploymentDto = null;
151
151
  string devKey = null;
152
152
  {
153
+ List<string> platformStrings = new();
154
+ platformStrings.Add("Mac");
155
+ platformStrings.Add("Windows");
156
+ if (gameConfig.supportsMobile) {
157
+ platformStrings.Add("iOS");
158
+ platformStrings.Add("Android");
159
+ }
153
160
  var packageSlugs = gameConfig.packages.Select((p) => p.id);
154
161
  for (int i = 0; i < possibleKeys.Count; i++) {
155
162
  devKey = possibleKeys[i];
@@ -161,7 +168,8 @@ public class Deploy {
161
168
  defaultScene = gameConfig.startingScene.name,
162
169
  deployCode = true,
163
170
  deployAssets = platforms.Length > 0,
164
- packageSlugs = packageSlugs.ToArray()
171
+ packageSlugs = packageSlugs.ToArray(),
172
+ platforms = platformStrings.ToArray(),
165
173
  }), "application/json");
166
174
  req.SetRequestHeader("Authorization", "Bearer " + devKey);
167
175
  yield return req.SendWebRequest();
@@ -19,7 +19,8 @@ namespace Code.Bootstrap {
19
19
  AirshipPlatform.iOS,
20
20
  AirshipPlatform.Mac,
21
21
  AirshipPlatform.Windows,
22
- AirshipPlatform.Linux
22
+ AirshipPlatform.Linux,
23
+ AirshipPlatform.Android,
23
24
  };
24
25
 
25
26
  public static string GetStringName(AirshipPlatform platform) {
@@ -464,7 +464,7 @@ GameObject:
464
464
  - component: {fileID: 6904609107978231724}
465
465
  - component: {fileID: 7072641826937089519}
466
466
  m_Layer: 5
467
- m_Name: Image
467
+ m_Name: BottomCard
468
468
  m_TagString: Untagged
469
469
  m_Icon: {fileID: 0}
470
470
  m_NavMeshLayer: 0
@@ -480,8 +480,10 @@ RectTransform:
480
480
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
481
481
  m_LocalPosition: {x: 0, y: 0, z: 0}
482
482
  m_LocalScale: {x: 0.92927, y: 0.92927, z: 0.92927}
483
- m_ConstrainProportionsScale: 0
483
+ m_ConstrainProportionsScale: 1
484
484
  m_Children:
485
+ - {fileID: 4779218538534976006}
486
+ - {fileID: 1580868800969815117}
485
487
  - {fileID: 501496119120443493}
486
488
  - {fileID: 4779218537312054114}
487
489
  - {fileID: 7042672436343788158}
@@ -942,9 +944,7 @@ RectTransform:
942
944
  m_Children:
943
945
  - {fileID: 4779218538547314202}
944
946
  - {fileID: 6709306793874349174}
945
- - {fileID: 1580868800969815117}
946
947
  - {fileID: 6349396839246893691}
947
- - {fileID: 4779218538534976006}
948
948
  - {fileID: 2046571341396879633}
949
949
  m_Father: {fileID: 0}
950
950
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -1034,6 +1034,7 @@ MonoBehaviour:
1034
1034
  spinner: {fileID: 4779218537929143458}
1035
1035
  gameImage: {fileID: 5080459163667922316}
1036
1036
  editorGameImageColor: {r: 0.14509805, g: 0.14509805, b: 0.14509805, a: 1}
1037
+ bottomCard: {fileID: 2046571341396879633}
1037
1038
  voiceChatCard: {fileID: 6349396839246893691}
1038
1039
  voiceChatToggle: {fileID: 2165061502823066488}
1039
1040
  updatedByGame: 0
@@ -1078,17 +1079,17 @@ RectTransform:
1078
1079
  m_GameObject: {fileID: 4779218538534976007}
1079
1080
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
1080
1081
  m_LocalPosition: {x: 0, y: 0, z: 0}
1081
- m_LocalScale: {x: 0.92927, y: 0.92927, z: 0.92927}
1082
+ m_LocalScale: {x: 1, y: 1, z: 1}
1082
1083
  m_ConstrainProportionsScale: 1
1083
1084
  m_Children:
1084
1085
  - {fileID: 4779218536769478307}
1085
- m_Father: {fileID: 4779218538432630284}
1086
+ m_Father: {fileID: 2046571341396879633}
1086
1087
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
1087
1088
  m_AnchorMin: {x: 0.5, y: 0}
1088
1089
  m_AnchorMax: {x: 0.5, y: 0}
1089
- m_AnchoredPosition: {x: 0, y: 116}
1090
+ m_AnchoredPosition: {x: 0, y: -36.94728}
1090
1091
  m_SizeDelta: {x: 129.32092, y: 39.91571}
1091
- m_Pivot: {x: 0.5, y: 0}
1092
+ m_Pivot: {x: 0.5, y: 0.5}
1092
1093
  --- !u!222 &4779218538534976003
1093
1094
  CanvasRenderer:
1094
1095
  m_ObjectHideFlags: 0
@@ -1846,7 +1847,7 @@ PrefabInstance:
1846
1847
  serializedVersion: 2
1847
1848
  m_Modification:
1848
1849
  serializedVersion: 3
1849
- m_TransformParent: {fileID: 4779218538432630284}
1850
+ m_TransformParent: {fileID: 2046571341396879633}
1850
1851
  m_Modifications:
1851
1852
  - target: {fileID: 2203551379012122545, guid: f4a774ea871d94897b410c2210fd5105, type: 3}
1852
1853
  propertyPath: m_Name
@@ -1882,7 +1883,7 @@ PrefabInstance:
1882
1883
  objectReference: {fileID: 0}
1883
1884
  - target: {fileID: 6131290041883517747, guid: f4a774ea871d94897b410c2210fd5105, type: 3}
1884
1885
  propertyPath: m_AnchorMax.y
1885
- value: 0.5
1886
+ value: 1
1886
1887
  objectReference: {fileID: 0}
1887
1888
  - target: {fileID: 6131290041883517747, guid: f4a774ea871d94897b410c2210fd5105, type: 3}
1888
1889
  propertyPath: m_AnchorMin.x
@@ -1890,7 +1891,7 @@ PrefabInstance:
1890
1891
  objectReference: {fileID: 0}
1891
1892
  - target: {fileID: 6131290041883517747, guid: f4a774ea871d94897b410c2210fd5105, type: 3}
1892
1893
  propertyPath: m_AnchorMin.y
1893
- value: 0.5
1894
+ value: 1
1894
1895
  objectReference: {fileID: 0}
1895
1896
  - target: {fileID: 6131290041883517747, guid: f4a774ea871d94897b410c2210fd5105, type: 3}
1896
1897
  propertyPath: m_SizeDelta.x
@@ -1900,6 +1901,18 @@ PrefabInstance:
1900
1901
  propertyPath: m_SizeDelta.y
1901
1902
  value: 49.7565
1902
1903
  objectReference: {fileID: 0}
1904
+ - target: {fileID: 6131290041883517747, guid: f4a774ea871d94897b410c2210fd5105, type: 3}
1905
+ propertyPath: m_LocalScale.x
1906
+ value: 1.0761135
1907
+ objectReference: {fileID: 0}
1908
+ - target: {fileID: 6131290041883517747, guid: f4a774ea871d94897b410c2210fd5105, type: 3}
1909
+ propertyPath: m_LocalScale.y
1910
+ value: 1.0761135
1911
+ objectReference: {fileID: 0}
1912
+ - target: {fileID: 6131290041883517747, guid: f4a774ea871d94897b410c2210fd5105, type: 3}
1913
+ propertyPath: m_LocalScale.z
1914
+ value: 1.0761135
1915
+ objectReference: {fileID: 0}
1903
1916
  - target: {fileID: 6131290041883517747, guid: f4a774ea871d94897b410c2210fd5105, type: 3}
1904
1917
  propertyPath: m_LocalPosition.x
1905
1918
  value: 0
@@ -1934,7 +1947,7 @@ PrefabInstance:
1934
1947
  objectReference: {fileID: 0}
1935
1948
  - target: {fileID: 6131290041883517747, guid: f4a774ea871d94897b410c2210fd5105, type: 3}
1936
1949
  propertyPath: m_AnchoredPosition.y
1937
- value: -261
1950
+ value: 47
1938
1951
  objectReference: {fileID: 0}
1939
1952
  - target: {fileID: 6131290041883517747, guid: f4a774ea871d94897b410c2210fd5105, type: 3}
1940
1953
  propertyPath: m_LocalEulerAnglesHint.x
@@ -26,6 +26,7 @@ public class CoreLoadingScreen : BundleLoadingScreen
26
26
  public GameObject spinner;
27
27
  public RawImage gameImage;
28
28
  public Color editorGameImageColor;
29
+ public RectTransform bottomCard;
29
30
 
30
31
  [NonSerialized] private float startTime = 0f;
31
32
  [NonSerialized] private bool showedVoiceChatCard = false;
@@ -59,6 +60,10 @@ public class CoreLoadingScreen : BundleLoadingScreen
59
60
  if (Application.isEditor) {
60
61
  this.gameImage.enabled = false;
61
62
  }
63
+ #endif
64
+
65
+ #if UNITY_IOS || UNITY_ANDROID
66
+
62
67
  #endif
63
68
 
64
69
  this.startTime = 0f;
@@ -68,9 +73,8 @@ public class CoreLoadingScreen : BundleLoadingScreen
68
73
 
69
74
  var deviceInfo = DeviceBridge.GetDeviceType();
70
75
  if (deviceInfo is AirshipDeviceType.Phone or AirshipDeviceType.Tablet) {
71
- var t = this.disconnectButton.transform as RectTransform;
72
- t.anchoredPosition = new Vector2(-50, 50);
73
- t.localScale = new Vector3(0.5f, 0.5f, 0.5f);
76
+ this.bottomCard.localScale = Vector3.one * 1.1f;
77
+ this.bottomCard.anchoredPosition = new Vector2(0, 185);
74
78
  }
75
79
 
76
80
  _canvas.enabled = true;
@@ -83,7 +87,7 @@ public class CoreLoadingScreen : BundleLoadingScreen
83
87
  this.voiceChatToggle.onValueChanged += VoiceChatToggle_OnValueChanged;
84
88
 
85
89
  if (Application.isMobilePlatform) {
86
- this.disconnectButton.transform.localScale = new Vector3(1.2f, 1.2f, 1.2f);
90
+ this.disconnectButton.transform.localScale = new Vector3(1.3f, 1.2f, 1.2f);
87
91
  }
88
92
  }
89
93
 
@@ -89,8 +89,8 @@ public class AccessoryBuilder : MonoBehaviour {
89
89
  //print("AccessoryBuilder OnEnable: " + this.gameObject.name);
90
90
  meshCombiner.OnCombineComplete += OnMeshCombineCompleted;
91
91
 
92
- if (Application.isPlaying && this.rig.faceMesh.material.mainTexture == null) {
93
- this.rig.faceMesh.gameObject.SetActive(false);
92
+ if (Application.isPlaying && rig.faceMesh.material.mainTexture == null) {
93
+ rig.faceMesh.gameObject.SetActive(false);
94
94
  }
95
95
 
96
96
  // update list of accessories
@@ -257,12 +257,12 @@ public class AccessoryBuilder : MonoBehaviour {
257
257
 
258
258
  [HideFromTS]
259
259
  public ActiveAccessory[] LoadOutfit(AccessoryOutfit outfit) {
260
- this.currentOutfit = outfit;
260
+ currentOutfit = outfit;
261
261
 
262
- this.SetSkinColor(outfit.skinColor);
262
+ SetSkinColor(outfit.skinColor);
263
263
 
264
264
  if (outfit.faceDecal && outfit.faceDecal.decalTexture) {
265
- this.SetFaceTexture(outfit.faceDecal.decalTexture);
265
+ SetFaceTexture(outfit.faceDecal.decalTexture);
266
266
  }
267
267
 
268
268
  return AddRange(outfit.accessories);
@@ -290,7 +290,7 @@ public class AccessoryBuilder : MonoBehaviour {
290
290
  }
291
291
  }
292
292
 
293
- this.RemoveBySlot(accessoryTemplate.accessorySlot);
293
+ RemoveBySlot(accessoryTemplate.accessorySlot);
294
294
 
295
295
  var lods = new List<ActiveAccessory>();
296
296
  for (var lodLevel = 0; lodLevel < lodCount; lodLevel++) {
@@ -434,6 +434,7 @@ public class AccessoryBuilder : MonoBehaviour {
434
434
  propertyBlock.SetTexture(FaceBaseMapTexture, texture);
435
435
  rig.faceMesh.SetPropertyBlock(propertyBlock);
436
436
  rig.faceMesh.gameObject.SetActive(true);
437
+ rig.faceMesh.enabled = true;
437
438
  }
438
439
 
439
440
  public void UpdateCombinedMesh() {
@@ -528,7 +529,7 @@ public class AccessoryBuilder : MonoBehaviour {
528
529
  }
529
530
 
530
531
  // print("AccessoryBuilder MeshCombine: " + this.gameObject.name);
531
- meshCombiner.CombineMeshes(this.skinColor);
532
+ meshCombiner.CombineMeshes(skinColor);
532
533
  } else {
533
534
  // print("AccessoryBuilder Manual Rig Mapping: " + this.gameObject.name);
534
535
  MapAccessoriesToRig();
@@ -1,3 +1,4 @@
1
+ using UnityEditor;
1
2
  using UnityEngine;
2
3
  using UnityEngine.SceneManagement;
3
4
 
@@ -27,30 +28,17 @@ public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
27
28
  public static T Instance {
28
29
  get {
29
30
  if (_instance == null) {
30
- _instance = (T)FindObjectOfType (typeof(T));
31
+ _instance = (T) FindAnyObjectByType(typeof(T));
31
32
  if (_instance == null) {
32
-
33
- string goName = typeof(T).ToString ();
33
+ var go = new GameObject();
34
+ go.name = typeof(T).ToString();
34
35
 
35
- GameObject go = GameObject.Find(goName);
36
- if (go == null)
37
- {
38
- // var core = GameObject.Find("AirshipCore");
39
- // if (!core) {
40
- // core = new GameObject("AirshipCore");
41
- // }
42
- go = new GameObject();
43
- go.name = goName;
44
- go.hideFlags = HideFlags.HideAndDontSave;
45
-
46
- var coreScene = SceneManager.GetSceneByName("CoreScene");
47
- if (coreScene.IsValid()) {
48
- SceneManager.MoveGameObjectToScene(go, coreScene);
49
- }
36
+ var coreScene = SceneManager.GetSceneByName("CoreScene");
37
+ if (coreScene.IsValid()) {
38
+ SceneManager.MoveGameObjectToScene(go, coreScene);
50
39
  }
51
40
 
52
- _instance = go.AddComponent<T> ();
53
- // print("added component " + _instance.name);
41
+ _instance = go.AddComponent<T>();
54
42
  }
55
43
  }
56
44
  return _instance;
@@ -87,4 +75,10 @@ public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
87
75
  this.transform.parent = parentGO.transform;
88
76
  }
89
77
  }
78
+
79
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
80
+ private static void ResetStaticFields()
81
+ {
82
+ _instance = null;
83
+ }
90
84
  }
@@ -4,6 +4,7 @@ using System.Linq;
4
4
  using Assets.Airship.VoxelRenderer;
5
5
  using System.Collections.Generic;
6
6
  using System.IO;
7
+ using System.Threading.Tasks;
7
8
  using System.Xml;
8
9
  using JetBrains.Annotations;
9
10
  using UnityEngine;
@@ -314,6 +315,7 @@ public class VoxelBlocks : MonoBehaviour {
314
315
  [NonSerialized] public List<string> m_bundlePaths = null;
315
316
 
316
317
  [SerializeField] public List<VoxelBlockDefinionList> blockDefinionLists = new();
318
+ private TaskCompletionSource<bool> loadedTask = new TaskCompletionSource<bool>(false);
317
319
 
318
320
  public BlockDefinition GetBlock(BlockId index) {
319
321
 
@@ -478,6 +480,11 @@ public class VoxelBlocks : MonoBehaviour {
478
480
  }
479
481
 
480
482
  }
483
+
484
+ public async Task WaitForLoaded() {
485
+ if (loadedTask.Task.IsCompleted) return;
486
+ await loadedTask.Task;
487
+ }
481
488
 
482
489
  private void ParseQuarterBlock(BlockDefinition block) {
483
490
 
@@ -594,7 +601,6 @@ public class VoxelBlocks : MonoBehaviour {
594
601
  }
595
602
  }
596
603
  public void Load(bool loadTexturesDirectlyFromDisk = false) {
597
-
598
604
  //clear everything
599
605
  Clear();
600
606
 
@@ -1099,6 +1105,7 @@ public class VoxelBlocks : MonoBehaviour {
1099
1105
  }*/
1100
1106
  }
1101
1107
  // Profiler.EndSample();
1108
+ loadedTask.TrySetResult(true);
1102
1109
  }
1103
1110
 
1104
1111
  //Fix a voxel value up with its solid mask bit
Binary file
@@ -61,6 +61,7 @@ namespace Nobi.UiRoundedCorners {
61
61
  if (Application.isPlaying && !RunCore.IsClient()) return;
62
62
 
63
63
  image.material = null; //This makes so that when the component is removed, the UI material returns to null
64
+ // EditorUtility.ClearDirty(image);
64
65
 
65
66
  DestroyHelper.Destroy(material);
66
67
  image = null;
@@ -69,7 +70,9 @@ namespace Nobi.UiRoundedCorners {
69
70
 
70
71
  public void Validate() {
71
72
  if (material == null) {
72
- material = new Material(Shader.Find("UI/RoundedCorners/IndependentRoundedCorners"));
73
+ material = new Material(Shader.Find("UI/RoundedCorners/IndependentRoundedCorners")) {
74
+ hideFlags = HideFlags.DontSave
75
+ };
73
76
  }
74
77
 
75
78
  if (image == null) {
@@ -1,4 +1,5 @@
1
1
  using System;
2
+ using UnityEditor;
2
3
  using UnityEngine;
3
4
  using UnityEngine.UI;
4
5
 
@@ -28,6 +29,7 @@ namespace Nobi.UiRoundedCorners {
28
29
 
29
30
  if (image != null) {
30
31
  image.material = null; //This makes so that when the component is removed, the UI material returns to null
32
+ // EditorUtility.ClearDirty(image);
31
33
  }
32
34
 
33
35
  DestroyHelper.Destroy(material);
@@ -64,7 +66,9 @@ namespace Nobi.UiRoundedCorners {
64
66
  var shader = Shader.Find("UI/RoundedCorners/RoundedCorners");
65
67
  if (shader == null) return;
66
68
 
67
- material = new Material(shader);
69
+ material = new Material(shader) {
70
+ hideFlags = HideFlags.DontSave
71
+ };
68
72
  }
69
73
 
70
74
  if (image == null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gg.easy.airship",
3
- "version": "0.1.2107",
3
+ "version": "0.1.2108",
4
4
  "displayName": "Airship",
5
5
  "unity": "2021.3",
6
6
  "unityRelease": "12f1",