diff --git a/.gitignore b/.gitignore
index 702aa80..e54d387 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,10 @@ spatialos_worker_packages.json
build/
logs/
workers/unity/external-default-unityclient.log
+workers/unity/Assets/Plugins/Improbable/Editor/Generated/
+workers/unity/Assets/Plugins/Improbable/Editor/Generated.meta
+workers/unity/Assets/Plugins/Improbable/Generated/
+workers/unity/Assets/Plugins/Improbable/Generated.meta
+workers/unity/Assets/Plugins/Improbable/EntityPrefabs/
+workers/unity/Assets/Plugins/Improbable/EntityPrefabs.meta
+workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Generated.Code.*
diff --git a/README.md b/README.md
index bb0b681..e50932f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,3 @@
-**Note: This project runs on SpatialOS version 12.2.1 and not the latest version which is 13.0.0.**
-
# PiratesTutorial
![Pirates Screenshot](pirates-screenshot.jpg)
diff --git a/spatialos.json b/spatialos.json
index 4117fe3..3132c09 100644
--- a/spatialos.json
+++ b/spatialos.json
@@ -1,8 +1,8 @@
{
"name": "your_project_name_here",
"project_version": "1.0.0",
- "sdk_version": "12.2.1",
+ "sdk_version": "13.0.0",
"dependencies": [
- {"name": "standard_library", "version": "12.2.1"}
+ {"name": "standard_library", "version": "13.0.0"}
]
}
diff --git a/workers/unity/.gitignore b/workers/unity/.gitignore
index 14dfa34..784d328 100644
--- a/workers/unity/.gitignore
+++ b/workers/unity/.gitignore
@@ -1,14 +1,11 @@
# Improbable Unity Ignores
.spatialos/
spatialos_worker_packages.json
-build.json
.invoke.log
zip_toolbelt.log
external-default-unityclient.log
external-default-unityworker.log
-Improbable.meta
Assets/Improbable
-Assets/Plugins/Improbable
Assets/Plugins/x86/Improbable
Assets/Plugins/x86.meta
Assets/Plugins/x86_64/Improbable
diff --git a/workers/unity/Assets/Plugins/Improbable.meta b/workers/unity/Assets/Plugins/Improbable.meta
new file mode 100644
index 0000000..c28200e
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: f326997a2d8c07e4d98274717c87cf09
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem.meta
new file mode 100644
index 0000000..e75ad55
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: ac249586b46041340aee45d45b2c7718
+folderAsset: yes
+timeCreated: 1516628439
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor.meta
new file mode 100644
index 0000000..0a1b74c
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: a406347b4133bb2499a69a992a9d1526
+folderAsset: yes
+timeCreated: 1515594091
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration.meta
new file mode 100644
index 0000000..f090b32
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 875bc38aec3bbbd40addf5e19f18c87d
+folderAsset: yes
+timeCreated: 1516879369
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironment.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironment.cs
new file mode 100644
index 0000000..7535145
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironment.cs
@@ -0,0 +1,10 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ public enum BuildEnvironment
+ {
+ Local,
+ Cloud
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironment.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironment.cs.meta
new file mode 100644
index 0000000..600cfee
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironment.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: f0865b1f4f2046a4a480d9aef2bf6354
+timeCreated: 1515678457
\ No newline at end of file
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironmentConfig.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironmentConfig.cs
new file mode 100644
index 0000000..b1f2610
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironmentConfig.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using UnityEditor;
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ [Serializable]
+ public class BuildEnvironmentConfig
+ {
+ public SpatialBuildPlatforms BuildPlatforms = SpatialBuildPlatforms.Current;
+ public BuildOptions BuildOptions = 0;
+
+ [NonSerialized] public bool ShowBuildOptions = true;
+ [NonSerialized] public bool ShowBuildPlatforms = true;
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironmentConfig.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironmentConfig.cs.meta
new file mode 100644
index 0000000..bc729c8
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildEnvironmentConfig.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 9b73d6e624a04708b19836efaa1b5c65
+timeCreated: 1515591603
\ No newline at end of file
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildPaths.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildPaths.cs
new file mode 100644
index 0000000..925eeef
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildPaths.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.IO;
+using Improbable.Unity.Util;
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ public static class BuildPaths
+ {
+ public static readonly string PrefabResourcesDirectory =
+ PathUtil.Combine("Assets", "Improbable", "Generated", "Resources", "EntityPrefabs").ToUnityPath();
+
+ public static readonly string PrefabSourceDirectory = PathUtil.Combine("Assets", "EntityPrefabs").ToUnityPath();
+
+ public static string BuildScratchDirectory
+ {
+ get { return Path.GetFullPath(Path.Combine("build", "worker")); }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildPaths.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildPaths.cs.meta
new file mode 100644
index 0000000..4cd4164
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/BuildPaths.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: fba50f74fb02ae5458452f0314f709ea
+timeCreated: 1515413340
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/FallbackIndentLevelScope.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/FallbackIndentLevelScope.cs
new file mode 100644
index 0000000..182f797
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/FallbackIndentLevelScope.cs
@@ -0,0 +1,28 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+#if !UNITY_2017_3_OR_NEWER
+using System;
+using UnityEditor;
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ ///
+ /// Fallback for Unity 2017.3f1 feature: EditorGUI.IndentLevelScope
+ ///
+ public class FallbackIndentLevelScope : IDisposable
+ {
+ private readonly int indent;
+
+ public FallbackIndentLevelScope(int increment)
+ {
+ indent = EditorGUI.indentLevel;
+ EditorGUI.indentLevel += indent;
+ }
+
+ public void Dispose()
+ {
+ EditorGUI.indentLevel = indent;
+ }
+ }
+}
+#endif
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/FallbackIndentLevelScope.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/FallbackIndentLevelScope.cs.meta
new file mode 100644
index 0000000..87422a5
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/FallbackIndentLevelScope.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: ad8fd511165f02a4b87eba70852032fe
+timeCreated: 1516634254
+licenseType: Pro
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/GUIColorScope.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/GUIColorScope.cs
new file mode 100644
index 0000000..9bef653
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/GUIColorScope.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using UnityEngine;
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ class GUIColorScope : IDisposable
+ {
+ private readonly Color color;
+
+ public GUIColorScope(Color newColor)
+ {
+ color = GUI.color;
+ GUI.color = newColor;
+ }
+
+ public void Dispose()
+ {
+ GUI.color = color;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/GUIColorScope.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/GUIColorScope.cs.meta
new file mode 100644
index 0000000..d7f082a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/GUIColorScope.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: bd15769f3e8c3054cbd24cd67dc29714
+timeCreated: 1516892521
+licenseType: Pro
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/PlayerCompression.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/PlayerCompression.cs
new file mode 100644
index 0000000..b26aa00
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/PlayerCompression.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ ///
+ /// Indicate whether or not built-out players should be compressed.
+ ///
+ public enum PlayerCompression
+ {
+ Enabled,
+ Disabled
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/PlayerCompression.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/PlayerCompression.cs.meta
new file mode 100644
index 0000000..8492902
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/PlayerCompression.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: fc38f441295f4e238f5640a33fbb37c5
+timeCreated: 1515429509
\ No newline at end of file
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SceneItem.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SceneItem.cs
new file mode 100644
index 0000000..e0081d6
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SceneItem.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ internal class SceneItem
+ {
+ public readonly SceneAsset SceneAsset;
+ public bool Included;
+ private readonly bool exists;
+
+ public SceneItem(SceneAsset sceneAsset, bool included, SceneAsset[] inAssetDatabase)
+ {
+ SceneAsset = sceneAsset;
+ Included = included;
+ exists = inAssetDatabase.Contains(sceneAsset);
+ }
+
+ public static SceneItem Drawer(Rect position, SceneItem item)
+ {
+ using (item.exists ? null : new GUIColorScope(Color.red))
+ {
+ var positionWidth = position.width;
+ var labelWidth = GUI.skin.toggle.CalcSize(GUIContent.none).x + 5;
+
+ position.width = labelWidth;
+ item.Included = EditorGUI.Toggle(position, item.Included);
+
+ position.x += labelWidth;
+ position.width = positionWidth - labelWidth;
+
+ EditorGUI.ObjectField(position, item.SceneAsset, typeof(SceneAsset), false);
+ }
+
+ return item;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SceneItem.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SceneItem.cs.meta
new file mode 100644
index 0000000..28bdc56
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SceneItem.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: d7b8c20e979c4107a4b48bd19da441ab
+timeCreated: 1515691397
\ No newline at end of file
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObject.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObject.cs
new file mode 100644
index 0000000..7344ef6
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObject.cs
@@ -0,0 +1,118 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ public abstract class SingletonScriptableObject : ScriptableObject
+ where TSelf : SingletonScriptableObject
+ {
+ private static readonly List Instances =
+ new List();
+
+ public virtual void OnEnable()
+ {
+ if (!IsAnAsset())
+ {
+ // This is not an asset, so don't register it as an instance.
+ return;
+ }
+
+ var self = (TSelf) this;
+
+ if (Instances.Find(instance => instance != self))
+ {
+ Debug.LogErrorFormat("There are multiple copies of {0} present. Please pick one and delete the other.", SelfType);
+ }
+
+ if (!Instances.Contains(self))
+ {
+ Instances.Add(self);
+ }
+ }
+
+ protected bool IsAnAsset()
+ {
+ var assetPath = AssetDatabase.GetAssetPath(this);
+
+ // If there is an asset path, it is in assets.
+ return !string.IsNullOrEmpty(assetPath);
+ }
+
+ public void OnDisable()
+ {
+ if (!IsAnAsset())
+ {
+ return;
+ }
+
+ var self = (TSelf) this;
+
+ if (Instances.Contains(self))
+ {
+ Instances.Remove(self);
+ }
+ }
+
+ private static readonly Type SelfType = typeof(TSelf);
+
+ public static TSelf GetInstance()
+ {
+ // Clean up dead ones.
+ Instances.RemoveAll(item => item == null);
+
+ if (Instances.Count > 0)
+ {
+ return Instances[0];
+ }
+
+ if (SingletonScriptableObjectLoader.LoadingInstances.Contains(SelfType))
+ {
+ return null;
+ }
+
+ SingletonScriptableObjectLoader.LoadingInstances.Add(SelfType);
+
+ try
+ {
+ var allInstanceGuidsInAssetDatabase =
+ AssetDatabase.FindAssets("t:" + SelfType.Name);
+
+ foreach (var instanceGUID in allInstanceGuidsInAssetDatabase)
+ {
+ var instancePath = AssetDatabase.GUIDToAssetPath(instanceGUID);
+
+ var loadedInstance = AssetDatabase.LoadAssetAtPath(instancePath);
+
+ // onload should have been called here, but if not, ensure it's in the list.
+ if (loadedInstance == null)
+ {
+ continue;
+ }
+
+ if (Instances.Find(instance => instance != loadedInstance))
+ {
+ Debug.LogErrorFormat(
+ "There are multiple copies of {0} present. Please pick one and delete the other.",
+ SelfType);
+ }
+
+ if (!Instances.Contains(loadedInstance))
+ {
+ Instances.Add(loadedInstance);
+ }
+ }
+ }
+ finally
+ {
+ SingletonScriptableObjectLoader.LoadingInstances.Remove(SelfType);
+ }
+
+ return Instances.FirstOrDefault();
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObject.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObject.cs.meta
new file mode 100644
index 0000000..a093116
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObject.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 1036cced36af4c0d977785b69e0d0761
+timeCreated: 1515593337
\ No newline at end of file
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObjectLoader.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObjectLoader.cs
new file mode 100644
index 0000000..2e9338e
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObjectLoader.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ internal static class SingletonScriptableObjectLoader
+ {
+ internal static readonly HashSet LoadingInstances = new HashSet();
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObjectLoader.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObjectLoader.cs.meta
new file mode 100644
index 0000000..5423caa
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SingletonScriptableObjectLoader.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: e5962adf05954b8787059c9052726a98
+timeCreated: 1515593328
\ No newline at end of file
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialBuildPlatforms.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialBuildPlatforms.cs
new file mode 100644
index 0000000..9c0a62b
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialBuildPlatforms.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ [System.Flags]
+ public enum SpatialBuildPlatforms
+ {
+ Current = 1 << 0,
+ Windows32 = 1 << 1,
+ Windows64 = 1 << 2,
+ Linux = 1 << 3,
+ OSX = 1 << 4
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialBuildPlatforms.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialBuildPlatforms.cs.meta
new file mode 100644
index 0000000..e406f31
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialBuildPlatforms.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: e0882977395e42ebab12de768f0489b2
+timeCreated: 1515604481
\ No newline at end of file
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfiguration.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfiguration.cs
new file mode 100644
index 0000000..7a067ed
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfiguration.cs
@@ -0,0 +1,143 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ [CreateAssetMenu(fileName = "SpatialOS Build Configuration", menuName = CreateMenuPath)]
+ public class SpatialOSBuildConfiguration : SingletonScriptableObject
+ {
+ internal const string CreateMenuPath = "SpatialOS/Build Configuration";
+
+ [SerializeField] private bool isInitialised;
+
+ public WorkerBuildConfiguration[] WorkerBuildConfigurations;
+
+ public override void OnEnable()
+ {
+ base.OnEnable();
+
+ if (!isInitialised)
+ {
+ ResetToDefault();
+ }
+
+ if (IsAnAsset())
+ {
+ UpdateEditorScenesForBuild();
+ }
+ }
+
+ private void ResetToDefault()
+ {
+ // Build default settings
+ var client = new WorkerBuildConfiguration()
+ {
+ WorkerPlatform = WorkerPlatform.UnityClient,
+ ScenesForWorker = AssetDatabase.FindAssets("t:Scene")
+ .Select(AssetDatabase.GUIDToAssetPath)
+ .Where(path => path.Contains(WorkerPlatform.UnityClient.ToString()))
+ .Select(AssetDatabase.LoadAssetAtPath).ToArray(),
+ LocalBuildConfig = new BuildEnvironmentConfig()
+ {
+ BuildPlatforms = SpatialBuildPlatforms.Current,
+ BuildOptions = BuildOptions.Development
+ },
+ CloudBuildConfig = new BuildEnvironmentConfig()
+ {
+ BuildPlatforms = SpatialBuildPlatforms.Current
+ }
+ };
+
+ var worker = new WorkerBuildConfiguration()
+ {
+ WorkerPlatform = WorkerPlatform.UnityWorker,
+ ScenesForWorker = AssetDatabase.FindAssets("t:Scene")
+ .Select(AssetDatabase.GUIDToAssetPath)
+ .Where(path => path.Contains(WorkerPlatform.UnityWorker.ToString()))
+ .Select(AssetDatabase.LoadAssetAtPath).ToArray(),
+ LocalBuildConfig = new BuildEnvironmentConfig()
+ {
+ BuildPlatforms = SpatialBuildPlatforms.Current,
+ BuildOptions = BuildOptions.EnableHeadlessMode
+ },
+ CloudBuildConfig = new BuildEnvironmentConfig()
+ {
+ BuildPlatforms = SpatialBuildPlatforms.Linux,
+ BuildOptions = BuildOptions.EnableHeadlessMode
+ }
+ };
+
+ WorkerBuildConfigurations = new WorkerBuildConfiguration[]
+ {
+ client, worker
+ };
+
+ isInitialised = true;
+ }
+
+ private void OnValidate()
+ {
+ if (!isInitialised)
+ {
+ ResetToDefault();
+ }
+
+ if (IsAnAsset())
+ {
+ UpdateEditorScenesForBuild();
+ }
+ }
+
+ private SceneAsset[] GetScenesForWorker(WorkerPlatform workerPlatform)
+ {
+ WorkerBuildConfiguration configurationForWorker = null;
+
+ if (WorkerBuildConfigurations != null)
+ {
+ configurationForWorker =
+ WorkerBuildConfigurations.FirstOrDefault(config => config.WorkerPlatform == workerPlatform);
+ }
+
+ return configurationForWorker == null
+ ? new SceneAsset[0]
+ : configurationForWorker.ScenesForWorker;
+ }
+
+ internal void UpdateEditorScenesForBuild()
+ {
+ EditorApplication.delayCall += () =>
+ {
+ EditorBuildSettings.scenes =
+ GetScenesForWorker(WorkerPlatform.UnityClient)
+ .Union(GetScenesForWorker(WorkerPlatform.UnityWorker))
+ .Select(AssetDatabase.GetAssetPath)
+ .Select(scenePath => new EditorBuildSettingsScene(scenePath, true))
+ .ToArray();
+ };
+ }
+
+ public BuildEnvironmentConfig GetEnvironmentConfigForWorker(WorkerPlatform platform,
+ BuildEnvironment targetEnvironment)
+ {
+ var config = WorkerBuildConfigurations.FirstOrDefault(x => x.WorkerPlatform == platform);
+ if (config == null)
+ {
+ throw new ArgumentException("Unknown WorkerPlatform " + platform);
+ }
+
+ return config.GetEnvironmentConfig(targetEnvironment);
+ }
+
+ public string[] GetScenePathsForWorker(WorkerPlatform workerType)
+ {
+ return GetScenesForWorker(workerType)
+ .Where(sceneAsset => sceneAsset != null)
+ .Select(AssetDatabase.GetAssetPath)
+ .ToArray();
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfiguration.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfiguration.cs.meta
new file mode 100644
index 0000000..cbcaebb
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfiguration.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: add92caab7fb7f24589c2e7198c19491
+timeCreated: 1513698470
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfigurationEditor.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfigurationEditor.cs
new file mode 100644
index 0000000..00e64c4
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfigurationEditor.cs
@@ -0,0 +1,419 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ [CustomEditor(typeof(SpatialOSBuildConfiguration))]
+ public class SpatialOSBuildConfigurationEditor : UnityEditor.Editor
+ {
+ private const int ScreenWidthForHorizontalLayout = 450;
+
+ private SceneAsset[] scenesInAssetDatabase;
+
+ public void OnEnable()
+ {
+ scenesInAssetDatabase = AssetDatabase.FindAssets("t:Scene")
+ .Select(AssetDatabase.GUIDToAssetPath)
+ .Select(AssetDatabase.LoadAssetAtPath).ToArray();
+ }
+
+ private bool scenesChanged;
+
+ public override void OnInspectorGUI()
+ {
+ var workerConfiguration = (SpatialOSBuildConfiguration) target;
+
+ scenesChanged = false;
+
+ var configs = workerConfiguration.WorkerBuildConfigurations;
+ foreach (WorkerBuildConfiguration workerConfig in configs)
+ {
+ DrawWorkerConfiguration(workerConfig);
+ }
+
+ if (scenesChanged)
+ {
+ scenesChanged = false;
+ workerConfiguration.UpdateEditorScenesForBuild();
+ }
+ }
+
+ private void DrawWorkerConfiguration(WorkerBuildConfiguration configurationForWorker)
+ {
+ var platformName = configurationForWorker.WorkerPlatform.ToString();
+ configurationForWorker.ShowFoldout = EditorGUILayout.Foldout(configurationForWorker.ShowFoldout, platformName);
+
+ if (configurationForWorker.ShowFoldout)
+ {
+ using (IndentLevelScope(1))
+ {
+ DrawScenesInspectorForWorker(configurationForWorker);
+ DrawEnvironmentInspectorForWorker(configurationForWorker);
+ }
+ }
+ }
+
+ [Flags]
+ private enum ReorderableListFlags
+ {
+ None = 0,
+ ShowIndices = 1 << 0,
+ EnableReordering = 1 << 1,
+ }
+
+ private void DrawScenesInspectorForWorker(WorkerBuildConfiguration configurationForWorker)
+ {
+ EditorGUILayout.LabelField("Scenes", EditorStyles.boldLabel);
+
+ using (IndentLevelScope(1))
+ {
+ var scenesToShowInList = configurationForWorker
+ .ScenesForWorker
+ .Select((sceneAsset, index) => new SceneItem(sceneAsset, true, scenesInAssetDatabase))
+ .ToList();
+
+ EditorGUI.BeginChangeCheck();
+
+ var sceneItems = scenesInAssetDatabase
+ .Where(sceneAsset => !configurationForWorker.ScenesForWorker.Contains(sceneAsset))
+ .Select(sceneAsset => new SceneItem(sceneAsset, false, scenesInAssetDatabase)).ToList();
+
+ var horizontalLayout = Screen.width > ScreenWidthForHorizontalLayout;
+
+ using (horizontalLayout ? new EditorGUILayout.HorizontalScope() : null)
+ {
+ using (horizontalLayout ? new EditorGUILayout.VerticalScope() : null)
+ {
+ EditorGUILayout.LabelField("Scenes to include (in order)");
+
+ DrawIndentedList(scenesToShowInList, ReorderableListFlags.ShowIndices | ReorderableListFlags.EnableReordering,
+ SceneItem.Drawer);
+ }
+
+ using (horizontalLayout ? new EditorGUILayout.VerticalScope() : null)
+ {
+ using (horizontalLayout ? IndentLevelScope(-EditorGUI.indentLevel) : null)
+ {
+ EditorGUILayout.LabelField("Exclude");
+
+ DrawIndentedList(sceneItems,
+ ReorderableListFlags.None,
+ SceneItem.Drawer);
+ }
+ }
+ }
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ EditorUtility.SetDirty(target);
+ Undo.RecordObject(target, "Configure scenes for worker");
+
+ configurationForWorker.ScenesForWorker = scenesToShowInList.Concat(sceneItems)
+ .Where(item => item.Included)
+ .Select(item => item.SceneAsset).ToArray();
+
+ scenesChanged = true;
+ }
+ }
+ }
+
+ private void DrawEnvironmentInspectorForWorker(WorkerBuildConfiguration configurationForWorker)
+ {
+ EditorGUILayout.LabelField("Environments", EditorStyles.boldLabel);
+
+ DrawEnvironmentInspector(BuildEnvironment.Local, configurationForWorker);
+ DrawEnvironmentInspector(BuildEnvironment.Cloud, configurationForWorker);
+ }
+
+ private static TEnum EnumFlagsToggleField(TEnum source) where TEnum : struct, IConvertible
+ {
+ return EnumFlagsToggleField(source, SimpleToString);
+ }
+
+ private static TEnum EnumFlagsToggleField(TEnum source, Func nameFunction)
+ where TEnum : struct, IConvertible
+ {
+ if (!typeof(TEnum).IsEnum)
+ {
+ throw new ArgumentException("TEnum must be an enum type");
+ }
+
+ var enumNonZeroValues = Enum.GetValues(typeof(TEnum)).Cast()
+ .Where(options => options.ToInt32(NumberFormatInfo.CurrentInfo) != 0).ToArray();
+
+ using (IndentLevelScope(1))
+ {
+ var indentedHelpBox = new GUIStyle(EditorStyles.helpBox) { margin = { left = EditorGUI.indentLevel * 16 } };
+
+ using (new EditorGUILayout.VerticalScope(indentedHelpBox))
+ using (IndentLevelScope(-EditorGUI.indentLevel))
+ {
+ var sourceBitValue = source.ToInt32(NumberFormatInfo.CurrentInfo);
+
+ foreach (var enumValue in enumNonZeroValues)
+ {
+ var targetBitValue = enumValue.ToInt32(NumberFormatInfo.CurrentInfo);
+
+ var hasFlag = (sourceBitValue & targetBitValue) != 0;
+
+ var newFlag = EditorGUILayout.ToggleLeft(nameFunction(enumValue), hasFlag);
+
+ if (hasFlag != newFlag)
+ {
+ // the flag has changed, doing an XOR will flip that value
+ source = (TEnum) (object) (sourceBitValue ^ targetBitValue);
+ }
+ }
+ }
+ }
+
+ return source;
+ }
+
+ private void DrawEnvironmentInspector(BuildEnvironment environment,
+ WorkerBuildConfiguration configurationForWorker)
+ {
+ using (IndentLevelScope(1))
+ {
+ EditorGUILayout.LabelField(environment.ToString());
+
+ var environmentConfiguration =
+ configurationForWorker.GetEnvironmentConfig(environment);
+
+ ConfigureBuildPlatforms(environmentConfiguration);
+ ConfigureBuildOptions(environmentConfiguration);
+ }
+ }
+
+ private void ConfigureBuildOptions(BuildEnvironmentConfig environmentConfiguration)
+ {
+ using (IndentLevelScope(1))
+ {
+ var buildOptionsString = EnumFlagToString(environmentConfiguration.BuildOptions);
+
+ EditorGUI.BeginChangeCheck();
+
+ var showBuildOptions = EditorGUILayout.Foldout(environmentConfiguration.ShowBuildOptions,
+ "Build Options: " + buildOptionsString);
+
+ BuildOptions newBuildOptions = environmentConfiguration.BuildOptions;
+
+ if (showBuildOptions)
+ {
+ newBuildOptions = EnumFlagsToggleField(environmentConfiguration.BuildOptions);
+ }
+
+ if ((newBuildOptions & BuildOptions.EnableHeadlessMode) != 0 &&
+ (newBuildOptions & BuildOptions.Development) != 0)
+ {
+ EditorGUILayout.HelpBox(
+ "\nYou cannot have both EnableHeadlessMode and Development in BuildOptions.\n\n" +
+ "This will crash Unity Editor while building.\n",
+ MessageType.Error);
+ }
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ // build options have changed
+
+ EditorUtility.SetDirty(target);
+ Undo.RecordObject(target, "Configure build options for worker");
+
+ environmentConfiguration.ShowBuildOptions = showBuildOptions;
+ environmentConfiguration.BuildOptions = newBuildOptions;
+ }
+ }
+ }
+
+ private static string EnumFlagToString(TEnum value) where TEnum : struct, IConvertible
+ {
+ return EnumFlagToString(value, SimpleToString);
+ }
+
+ private static string SimpleToString(TValue activeValue) where TValue : IConvertible
+ {
+ return activeValue.ToString(CultureInfo.InvariantCulture);
+ }
+
+ private static string EnumFlagToString(TEnum value, Func nameFunction)
+ where TEnum : struct, IConvertible
+ {
+ if (!typeof(TEnum).IsEnum)
+ {
+ throw new ArgumentException("TEnum must be an enum type");
+ }
+
+ var enumNonZeroValues = Enum.GetValues(typeof(TEnum)).Cast()
+ .Where(options => options.ToInt32(NumberFormatInfo.CurrentInfo) != 0).ToArray();
+
+ var sourceBitValue = value.ToInt32(NumberFormatInfo.CurrentInfo);
+
+ if (sourceBitValue == 0)
+ {
+ return "None";
+ }
+
+ return string.Join(", ",
+ enumNonZeroValues
+ .Where(enumValue => (sourceBitValue & enumValue.ToInt32(NumberFormatInfo.CurrentInfo)) != 0)
+ .Select(nameFunction).ToArray());
+ }
+
+ private void ConfigureBuildPlatforms(BuildEnvironmentConfig environmentConfiguration)
+ {
+ using (IndentLevelScope(1))
+ {
+ var buildPlatformsString = EnumFlagToString(environmentConfiguration.BuildPlatforms,
+ BuildPlatformToString);
+
+ EditorGUI.BeginChangeCheck();
+
+ var showBuildPlatforms = EditorGUILayout.Foldout(environmentConfiguration.ShowBuildPlatforms,
+ "Build Platforms: " + buildPlatformsString);
+
+ SpatialBuildPlatforms newBuildPlatforms = environmentConfiguration.BuildPlatforms;
+
+ if (showBuildPlatforms)
+ {
+ newBuildPlatforms = EnumFlagsToggleField(environmentConfiguration.BuildPlatforms,
+ BuildPlatformToString);
+ }
+
+ var currentAdjustedPlatforms = newBuildPlatforms;
+
+ if ((currentAdjustedPlatforms & SpatialBuildPlatforms.Current) != 0)
+ {
+ currentAdjustedPlatforms &= ~SpatialBuildPlatforms.Current;
+ currentAdjustedPlatforms |= WorkerBuilder.GetCurrentBuildPlatform();
+ }
+
+ if ((currentAdjustedPlatforms & SpatialBuildPlatforms.Windows32) != 0 &&
+ (currentAdjustedPlatforms & SpatialBuildPlatforms.Windows64) != 0)
+ {
+ EditorGUILayout.HelpBox(
+ "\n" + WorkerBuilder.IncompatibleWindowsPlatformsErrorMessage + "\n",
+ MessageType.Error);
+ }
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ // build platforms have changed
+ EditorUtility.SetDirty(target);
+ Undo.RecordObject(target, "Configure build platforms for worker");
+
+ environmentConfiguration.ShowBuildPlatforms = showBuildPlatforms;
+ environmentConfiguration.BuildPlatforms = newBuildPlatforms;
+ }
+ }
+ }
+
+ private static string BuildPlatformToString(SpatialBuildPlatforms value)
+ {
+ if (value == SpatialBuildPlatforms.Current)
+ {
+ return string.Format("Current ({0})", WorkerBuilder.GetCurrentBuildPlatform());
+ }
+
+ return value.ToString();
+ }
+
+ private static readonly GUIContent MoveUpButtonContents = new GUIContent("^", "Move item up");
+ private static readonly GUIContent MoveDownButtonContents = new GUIContent("v", "Move item down");
+
+ private static void DrawIndentedList(IList list,
+ ReorderableListFlags flags, Func drawer)
+ {
+ if (list.Count == 0)
+ {
+ EditorGUILayout.HelpBox("No items in list", MessageType.Info);
+ return;
+ }
+
+ var enableReordering = (flags & ReorderableListFlags.EnableReordering) != 0;
+ var showIndices = (flags & ReorderableListFlags.ShowIndices) != 0;
+ var indentLevel = EditorGUI.indentLevel;
+
+ using (IndentLevelScope(-EditorGUI.indentLevel))
+ {
+ for (var i = 0; i < list.Count; i++)
+ {
+ var item = list[i];
+ var controlRect = EditorGUILayout.GetControlRect(false);
+
+ using (IndentLevelScope(indentLevel))
+ {
+ controlRect = EditorGUI.IndentedRect(controlRect);
+ }
+
+ if (showIndices)
+ {
+ var indexContent = new GUIContent(i.ToString());
+ var indexRect = new Rect(controlRect);
+ indexRect.width = GUI.skin.label.CalcSize(indexContent).x;
+
+ GUI.Label(indexRect, indexContent);
+
+ controlRect.x += indexRect.width + 5;
+ controlRect.width -= indexRect.width + 5;
+ }
+
+ var drawerRect = new Rect(controlRect);
+
+ if (enableReordering)
+ {
+ drawerRect.width -= 40;
+ }
+
+ drawer(drawerRect, item);
+
+ if (enableReordering)
+ {
+ var buttonRect = new Rect(controlRect);
+ buttonRect.x = buttonRect.xMax - 40;
+ buttonRect.width = 20;
+
+ using (new EditorGUI.DisabledScope(i == 0))
+ {
+ if (GUI.Button(buttonRect, MoveUpButtonContents))
+ {
+ SwapInList(list, i, i - 1);
+ }
+ }
+
+ buttonRect.x += 20;
+
+ using (new EditorGUI.DisabledScope(i == list.Count - 1))
+ {
+ if (GUI.Button(buttonRect, MoveDownButtonContents))
+ {
+ SwapInList(list, i, i + 1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static void SwapInList(IList list, int indexA, int indexB)
+ {
+ var temp = list[indexA];
+ list[indexA] = list[indexB];
+ list[indexB] = temp;
+ }
+
+ private static IDisposable IndentLevelScope(int increment)
+ {
+#if UNITY_2017_3_OR_NEWER
+ return new EditorGUI.IndentLevelScope(increment);
+#else
+ return new FallbackIndentLevelScope(increment);
+#endif
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfigurationEditor.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfigurationEditor.cs.meta
new file mode 100644
index 0000000..1092087
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/SpatialOSBuildConfigurationEditor.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5b7f35f2a6dae6b43baacb662601de3d
+timeCreated: 1513779752
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildConfiguration.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildConfiguration.cs
new file mode 100644
index 0000000..e7f022a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildConfiguration.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using UnityEditor;
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ [Serializable]
+ public class WorkerBuildConfiguration
+ {
+ public WorkerPlatform WorkerPlatform;
+ public SceneAsset[] ScenesForWorker;
+
+ public BuildEnvironmentConfig LocalBuildConfig = new BuildEnvironmentConfig();
+ public BuildEnvironmentConfig CloudBuildConfig = new BuildEnvironmentConfig();
+
+ public BuildEnvironmentConfig GetEnvironmentConfig(BuildEnvironment targetEnvironment)
+ {
+ BuildEnvironmentConfig buildEnvironmentConfig;
+
+ switch (targetEnvironment)
+ {
+ case BuildEnvironment.Local:
+ buildEnvironmentConfig = LocalBuildConfig;
+ break;
+ case BuildEnvironment.Cloud:
+ buildEnvironmentConfig = CloudBuildConfig;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("targetEnvironment", targetEnvironment, null);
+ }
+
+ return buildEnvironmentConfig;
+ }
+
+ [NonSerialized] public bool ShowFoldout = true;
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildConfiguration.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildConfiguration.cs.meta
new file mode 100644
index 0000000..f3de497
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildConfiguration.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 635648f6704340679b4166330a3d2c97
+timeCreated: 1513698790
\ No newline at end of file
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildData.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildData.cs
new file mode 100644
index 0000000..79d783a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildData.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using Improbable.Unity.Util;
+using UnityEditor;
+
+namespace Improbable.Unity.MinimalBuildSystem.Configuration
+{
+ public class WorkerBuildData
+ {
+ internal const BuildTarget OSXBuildTarget =
+#if UNITY_2017_3_OR_NEWER
+ BuildTarget.StandaloneOSX;
+#else
+ BuildTarget.StandaloneOSXIntel64;
+#endif
+
+ private readonly WorkerPlatform workerPlatform;
+ private readonly BuildTarget buildTarget;
+
+ private static readonly Dictionary BuildTargetNames =
+ new Dictionary
+ {
+ { BuildTarget.StandaloneWindows, "Windows" },
+ { BuildTarget.StandaloneWindows64, "Windows" },
+ { BuildTarget.StandaloneLinux64, "Linux" },
+ { OSXBuildTarget, "Mac" }
+ };
+
+ private static readonly Dictionary BuildPlatformExtensions =
+ new Dictionary
+ {
+ { BuildTarget.StandaloneWindows, ".exe" },
+ { BuildTarget.StandaloneWindows64, ".exe" },
+ { BuildTarget.StandaloneLinux64, "" },
+ { OSXBuildTarget, "" }
+ };
+
+ public WorkerBuildData(WorkerPlatform workerPlatform, BuildTarget buildTarget)
+ {
+ switch (workerPlatform)
+ {
+ case WorkerPlatform.UnityWorker:
+ case WorkerPlatform.UnityClient:
+ break;
+ default:
+ throw new ArgumentException("Unsupported WorkerPlatform " + workerPlatform);
+ }
+
+ if (!BuildTargetNames.ContainsKey(buildTarget))
+ {
+ throw new ArgumentException("Unsupported BuildPlatform " + workerPlatform);
+ }
+
+ this.workerPlatform = workerPlatform;
+ this.buildTarget = buildTarget;
+ }
+
+ private string BuildTargetName
+ {
+ get { return BuildTargetNames[buildTarget]; }
+ }
+
+ public string BuildScratchDirectory
+ {
+ get { return PathUtil.Combine(BuildPaths.BuildScratchDirectory, PackageName, ExecutableName).ToUnityPath(); }
+ }
+
+ public string WorkerPlatformName
+ {
+ get { return workerPlatform.ToString(); }
+ }
+
+ private string ExecutableName
+ {
+ get { return PackageName + BuildPlatformExtensions[buildTarget]; }
+ }
+
+ public string PackageName
+ {
+ get { return string.Format("{0}@{1}", workerPlatform, BuildTargetName); }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildData.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildData.cs.meta
new file mode 100644
index 0000000..4619e4f
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/Configuration/WorkerBuildData.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: cd14637da710af84ab5725fefe45fb37
+timeCreated: 1515422041
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/EntityPrefabs.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/EntityPrefabs.cs
new file mode 100644
index 0000000..33299b9
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/EntityPrefabs.cs
@@ -0,0 +1,93 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Improbable.Unity.Assets;
+using Improbable.Unity.MinimalBuildSystem.Configuration;
+using Improbable.Unity.Util;
+using UnityEditor;
+using UnityEditor.Build;
+using UnityEngine;
+
+namespace Improbable.Unity.MinimalBuildSystem
+{
+ public static class EntityPrefabs
+ {
+ public static void Export(WorkerPlatform platform)
+ {
+ Clean();
+
+ var prefabPaths = GetAllPrefabAssetPaths();
+ var copies = CopyPrefabs(prefabPaths);
+ CompilePrefabs(copies, platform);
+
+ AssetDatabase.SaveAssets();
+ Resources.UnloadUnusedAssets();
+ }
+
+ public static void Clean()
+ {
+ FileUtil.DeleteFileOrDirectory(BuildPaths.PrefabResourcesDirectory);
+ FileUtil.DeleteFileOrDirectory(BuildPaths.PrefabResourcesDirectory + ".meta");
+
+ AssetDatabase.Refresh();
+ }
+
+ private static void CompilePrefabs(List paths, WorkerPlatform platform)
+ {
+ var progress = 0;
+ foreach (var path in paths)
+ {
+ EditorUtility.DisplayProgressBar("Processing EntityPrefabs", "Preparing",
+ progress / (float) paths.Count());
+ progress++;
+
+ var compiler = new PrefabCompiler(platform);
+ var prefab = AssetDatabase.LoadAssetAtPath(path);
+ compiler.Compile(prefab);
+ }
+
+ EditorUtility.ClearProgressBar();
+ }
+
+ private static List CopyPrefabs(List paths)
+ {
+ AssetDatabase.StartAssetEditing();
+ PathUtil.EnsureDirectoryExists(BuildPaths.PrefabResourcesDirectory);
+ List newPaths = new List();
+ var progress = 0;
+
+ try
+ {
+ foreach (var path in paths)
+ {
+ EditorUtility.DisplayProgressBar("Processing EntityPrefabs", "Preparing",
+ progress / (float) paths.Count());
+ progress++;
+
+ var targetPath = PathUtil.Combine(BuildPaths.PrefabResourcesDirectory, Path.GetFileName(path));
+ if (!AssetDatabase.CopyAsset(path, targetPath))
+ {
+ throw new BuildFailedException("Failed to process EntityPrefab " + path);
+ }
+
+ newPaths.Add(targetPath);
+ }
+ }
+ finally
+ {
+ AssetDatabase.StopAssetEditing();
+ EditorUtility.ClearProgressBar();
+ }
+
+ return newPaths;
+ }
+
+ private static List GetAllPrefabAssetPaths()
+ {
+ return AssetDatabase.FindAssets("t:prefab", new[] { BuildPaths.PrefabSourceDirectory })
+ .Select(AssetDatabase.GUIDToAssetPath).ToList();
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/EntityPrefabs.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/EntityPrefabs.cs.meta
new file mode 100644
index 0000000..4e90126
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/EntityPrefabs.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: e65897c3f389c284996fd9e8e0cec76e
+timeCreated: 1492805719
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/MinimalBuildSystemMenu.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/MinimalBuildSystemMenu.cs
new file mode 100644
index 0000000..8ead430
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/MinimalBuildSystemMenu.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.Collections.Generic;
+using Improbable.Unity.MinimalBuildSystem.Configuration;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.MinimalBuildSystem
+{
+ static class MinimalBuildSystemMenu
+ {
+ private const string MinimalBuildMenu = "Improbable/Experimental Build";
+
+ [MenuItem(MinimalBuildMenu + "/Build UnityClient for local", false, 1)]
+ public static void BuildLocalClient()
+ {
+ MenuBuild(new[] { WorkerPlatform.UnityClient }, BuildEnvironment.Local);
+ }
+
+ [MenuItem(MinimalBuildMenu + "/Build UnityWorker for local", false, 2)]
+ public static void BuildLocalWorker()
+ {
+ MenuBuild(new[] { WorkerPlatform.UnityWorker }, BuildEnvironment.Local);
+ }
+
+ [MenuItem(MinimalBuildMenu + "/Build all workers for local", false, 3)]
+ public static void BuildLocalAll()
+ {
+ MenuBuild(new[] { WorkerPlatform.UnityWorker, WorkerPlatform.UnityClient }, BuildEnvironment.Local);
+ }
+
+ [MenuItem(MinimalBuildMenu + "/Build UnityClient for cloud", false, 14)]
+ public static void BuildCloudClient()
+ {
+ MenuBuild(new[] { WorkerPlatform.UnityClient }, BuildEnvironment.Cloud);
+ }
+
+ [MenuItem(MinimalBuildMenu + "/Build UnityWorker for cloud", false, 15)]
+ public static void BuildCloudWorker()
+ {
+ MenuBuild(new[] { WorkerPlatform.UnityWorker }, BuildEnvironment.Cloud);
+ }
+
+ [MenuItem(MinimalBuildMenu + "/Build all workers for cloud", false, 16)]
+ public static void BuildCloudAll()
+ {
+ MenuBuild(new[] { WorkerPlatform.UnityClient, WorkerPlatform.UnityWorker }, BuildEnvironment.Cloud);
+ }
+
+ [MenuItem(MinimalBuildMenu + "/Clean all workers", false, 27)]
+ public static void Clean()
+ {
+ WorkerBuilder.Clean();
+ Debug.Log("Clean completed");
+ }
+
+ private static void MenuBuild(IEnumerable platforms, BuildEnvironment environment)
+ {
+ // Delaying build by a frame to ensure the editor has re-rendered the UI to avoid odd glitches.
+ EditorApplication.delayCall += () =>
+ {
+ Debug.Log("Generating build configuration");
+ SpatialCommands.GenerateBuildConfiguration();
+ foreach (var platform in platforms)
+ {
+ WorkerBuilder.BuildWorkerForEnvironment(platform, environment);
+ }
+
+ Debug.LogFormat("Completed build for {0} target", environment);
+ };
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/MinimalBuildSystemMenu.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/MinimalBuildSystemMenu.cs.meta
new file mode 100644
index 0000000..44869a8
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/MinimalBuildSystemMenu.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: c867eb2bf6ee4ac8b437eba48bc1cfab
+timeCreated: 1513953870
\ No newline at end of file
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommandRunner.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommandRunner.cs
new file mode 100644
index 0000000..647b33a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommandRunner.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Diagnostics;
+using Improbable.Unity.Editor.Addons;
+
+namespace Improbable.Unity.MinimalBuildSystem
+{
+ internal static class SpatialCommandRunner
+ {
+ internal static void RunSpatialCommand(string args, string description)
+ {
+ var process = SpatialCommand.RunCommandWithSpatialInThePath(SpatialCommand.SpatialPath, new ProcessStartInfo
+ {
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ FileName = SpatialCommand.SpatialPath,
+ Arguments = args,
+ CreateNoWindow = true
+ });
+
+ var output = process.StandardOutput.ReadToEnd();
+ var errOut = process.StandardError.ReadToEnd();
+ process.WaitForExit();
+ if (process.ExitCode != 0)
+ {
+ throw new Exception(string.Format(
+ "Could not {2}. The following error occurred: {0}, {1}\n", output,
+ errOut, description));
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommandRunner.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommandRunner.cs.meta
new file mode 100644
index 0000000..659cab0
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommandRunner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: dc9c0a87316d45478a2dbfcf2bf38875
+timeCreated: 1515584015
\ No newline at end of file
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommands.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommands.cs
new file mode 100644
index 0000000..78acb64
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommands.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.IO;
+using Improbable.Unity.MinimalBuildSystem.Configuration;
+using Improbable.Unity.Util;
+
+namespace Improbable.Unity.MinimalBuildSystem
+{
+ internal static class SpatialCommands
+ {
+ #region Build Configuration
+
+ internal static void GenerateBuildConfiguration()
+ {
+ SpatialCommandRunner.RunSpatialCommand("build build-config", "generate build configuration");
+ }
+
+ #endregion Build Configuration
+
+ #region Zip
+
+ internal static void Zip(string zipAbsolutePath, string basePath, PlayerCompression compression)
+ {
+ ZipThroughSpatial(zipAbsolutePath, basePath, compression);
+ }
+
+ private static void ZipThroughSpatial(string zipAbsolutePath, string basePath, PlayerCompression useCompression)
+ {
+ var zipFileFullPath = Path.GetFullPath(zipAbsolutePath);
+
+ SpatialCommandRunner.RunSpatialCommand(ZipArgs(basePath, zipFileFullPath, useCompression),
+ string.Format("package the folder {0}", basePath));
+ }
+
+ private static string ZipArgs(string basePath, string zipFileFullPath,
+ PlayerCompression useCompression)
+ {
+ var filePattern = "**";
+ var subFolder = "";
+
+ return string.Format(
+ "file zip --output=\"{0}\" --basePath=\"{1}\" --relativePath=. \"{2}\" --compression={3}",
+ zipFileFullPath,
+ Path.GetFullPath(basePath),
+ PathUtil.EnsureTrailingSlash(subFolder) + filePattern,
+ useCompression == PlayerCompression.Enabled);
+ }
+
+ #endregion Zip
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommands.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommands.cs.meta
new file mode 100644
index 0000000..ee335f1
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/SpatialCommands.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 05a6667f572f4f3c8ccd27d1c14941bb
+timeCreated: 1515584173
\ No newline at end of file
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/WorkerBuilder.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/WorkerBuilder.cs
new file mode 100644
index 0000000..366651b
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/WorkerBuilder.cs
@@ -0,0 +1,266 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Improbable.Editor.Configuration;
+using Improbable.Unity.EditorTools.Util;
+using Improbable.Unity.MinimalBuildSystem.Configuration;
+using Improbable.Unity.Util;
+using UnityEditor;
+using UnityEditor.Build;
+using UnityEngine;
+
+namespace Improbable.Unity.MinimalBuildSystem
+{
+ public static class WorkerBuilder
+ {
+ private static readonly string PlayerBuildDirectory =
+ Path.GetFullPath(PathUtil.Combine(Directory.GetCurrentDirectory(), EditorPaths.AssetDatabaseDirectory,
+ "worker"));
+
+ ///
+ /// Build method that is invoked by commandline
+ ///
+ public static void Build()
+ {
+ try
+ {
+ var commandLine = Environment.GetCommandLineArgs();
+
+ Debug.LogFormat("Want to build with args: {0}", String.Join(", ", commandLine));
+
+ var buildTargetArg =
+ CommandLineUtil.GetCommandLineValue(commandLine, "buildTarget", "local");
+
+ if (string.IsNullOrEmpty(buildTargetArg))
+ {
+ // The default above does not get filled when -t parameter is not passed
+ buildTargetArg = BuildEnvironment.Local.ToString();
+ Debug.LogWarningFormat("Using default build target value: \"{0}\".", buildTargetArg);
+ }
+
+ BuildEnvironment buildEnvironment;
+
+ switch (buildTargetArg.ToLower())
+ {
+ case "cloud":
+ buildEnvironment = BuildEnvironment.Cloud;
+ break;
+ case "local":
+ buildEnvironment = BuildEnvironment.Local;
+ break;
+ default:
+ throw new BuildFailedException("Unknown build target value: " + buildTargetArg);
+ }
+
+ var workerTypesArg =
+ CommandLineUtil.GetCommandLineValue(commandLine, ConfigNames.BuildWorkerTypes,
+ "UnityClient,UnityWorker");
+
+ var wantedWorkerPlatforms = GetWorkerPlatforms(workerTypesArg);
+
+ SpatialCommands.GenerateBuildConfiguration();
+
+ foreach (var workerPlatform in wantedWorkerPlatforms)
+ {
+ BuildWorkerForEnvironment(workerPlatform, buildEnvironment);
+ }
+ }
+ catch (Exception e)
+ {
+ // Log the exception so it appears in the command line, and rethrow as a BuildFailedException so the build fails.
+ Debug.LogException(e);
+
+ if (e is BuildFailedException)
+ {
+ throw;
+ }
+
+ throw new BuildFailedException(e);
+ }
+ }
+
+ internal static WorkerPlatform[] GetWorkerPlatforms(string workerTypesArg)
+ {
+ WorkerPlatform[] wantedWorkerPlatforms;
+
+ try
+ {
+ wantedWorkerPlatforms = workerTypesArg.ToLower().Split(',')
+ .Select(workerType =>
+ {
+ switch (workerType.Trim())
+ {
+ case "unityclient":
+ return WorkerPlatform.UnityClient;
+ case "unityworker":
+ return WorkerPlatform.UnityWorker;
+ default:
+ throw new ArgumentException(
+ string.Format(
+ "The value '{0}' does not match any expected values: 'unityclient' or 'unityworker'.",
+ workerType));
+ }
+ })
+ .Distinct()
+ .ToArray();
+
+ Array.Sort(wantedWorkerPlatforms);
+ }
+ catch (ArgumentException innerException)
+ {
+ throw new ArgumentException(string.Format("Invalid argument: +{0} {1}\n{2}",
+ ConfigNames.BuildWorkerTypes, workerTypesArg, innerException.Message));
+ }
+
+ return wantedWorkerPlatforms;
+ }
+
+ public static void BuildWorkerForEnvironment(WorkerPlatform workerPlatform, BuildEnvironment targetEnvironment)
+ {
+ var spatialOSBuildConfiguration = GetBuildConfiguration();
+ var environmentConfig =
+ spatialOSBuildConfiguration.GetEnvironmentConfigForWorker(workerPlatform, targetEnvironment);
+ var buildPlatforms = environmentConfig.BuildPlatforms;
+ var buildOptions = environmentConfig.BuildOptions;
+
+ PathUtil.EnsureDirectoryExists(PlayerBuildDirectory);
+
+ foreach (var unityBuildTarget in GetUnityBuildTargets(buildPlatforms))
+ {
+ BuildWorkerForTarget(workerPlatform, unityBuildTarget, buildOptions, targetEnvironment);
+ }
+ }
+
+ internal const string IncompatibleWindowsPlatformsErrorMessage =
+ "Please choose only one of Windows32 or Windows64 as a build platform.";
+
+ internal static IEnumerable GetUnityBuildTargets(SpatialBuildPlatforms actualPlatforms)
+ {
+ List result = new List();
+
+ if ((actualPlatforms & SpatialBuildPlatforms.Current) != 0)
+ {
+ actualPlatforms |= GetCurrentBuildPlatform();
+ }
+
+ if ((actualPlatforms & SpatialBuildPlatforms.Linux) != 0)
+ {
+ result.Add(BuildTarget.StandaloneLinux64);
+ }
+
+ if ((actualPlatforms & SpatialBuildPlatforms.OSX) != 0)
+ {
+ result.Add(WorkerBuildData.OSXBuildTarget);
+ }
+
+ if ((actualPlatforms & SpatialBuildPlatforms.Windows32) != 0)
+ {
+ if ((actualPlatforms & SpatialBuildPlatforms.Windows64) != 0)
+ {
+ throw new Exception(IncompatibleWindowsPlatformsErrorMessage);
+ }
+
+ result.Add(BuildTarget.StandaloneWindows);
+ }
+
+ if ((actualPlatforms & SpatialBuildPlatforms.Windows64) != 0)
+ {
+ result.Add(BuildTarget.StandaloneWindows64);
+ }
+
+ return result.ToArray();
+ }
+
+ private static void BuildWorkerForTarget(WorkerPlatform workerPlatform, BuildTarget buildTarget,
+ BuildOptions buildOptions, BuildEnvironment targetEnvironment)
+ {
+ var spatialOSBuildConfiguration = GetBuildConfiguration();
+
+ Debug.LogFormat("Building \"{0}\" for worker platform: \"{1}\", environment: \"{2}\"", buildTarget,
+ workerPlatform, targetEnvironment);
+
+ EntityPrefabs.Export(workerPlatform);
+
+ var symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
+
+ try
+ {
+ var workerBuildData = new WorkerBuildData(workerPlatform, buildTarget);
+ var scenes = spatialOSBuildConfiguration.GetScenePathsForWorker(workerPlatform);
+
+ var typeSymbol = "IMPROBABLE_WORKERTYPE_" + workerBuildData.WorkerPlatformName.ToUpper();
+ var workerSymbols = symbols.Split(';')
+ .Concat(new[] { typeSymbol })
+ .Distinct()
+ .Aggregate((current, next) => current + ";" + next);
+ PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, workerSymbols);
+
+ BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions
+ {
+ options = buildOptions,
+ target = buildTarget,
+ scenes = scenes,
+ locationPathName = workerBuildData.BuildScratchDirectory
+ };
+
+ BuildPipeline.BuildPlayer(buildPlayerOptions);
+
+ var zipPath = Path.GetFullPath(Path.Combine(PlayerBuildDirectory, workerBuildData.PackageName));
+
+ var basePath = PathUtil.Combine(BuildPaths.BuildScratchDirectory, workerBuildData.PackageName);
+
+ SpatialCommands.Zip(zipPath, basePath,
+ targetEnvironment == BuildEnvironment.Local
+ ? PlayerCompression.Disabled
+ : PlayerCompression.Enabled);
+ }
+ finally
+ {
+ PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, symbols);
+ EntityPrefabs.Clean();
+ }
+ }
+
+ internal const string BuildConfigurationMissingErrorMessage =
+ "No objects of type SpatialOSBuildConfiguration found in the project.\nPlease create one using Assets/Create/" +
+ SpatialOSBuildConfiguration.CreateMenuPath + ".";
+
+ /// An instance of SpatialOSBuildConfiguration if one exists.
+ /// If no assets exist of type SpatialOSBuildConfiguration
+ private static SpatialOSBuildConfiguration GetBuildConfiguration()
+ {
+ var spatialOSBuildConfiguration = SpatialOSBuildConfiguration.GetInstance();
+
+ if (spatialOSBuildConfiguration == null)
+ {
+ throw new Exception(BuildConfigurationMissingErrorMessage);
+ }
+
+ return spatialOSBuildConfiguration;
+ }
+
+ internal static SpatialBuildPlatforms GetCurrentBuildPlatform()
+ {
+ switch (Application.platform)
+ {
+ case RuntimePlatform.WindowsEditor:
+ return SpatialBuildPlatforms.Windows64;
+ case RuntimePlatform.OSXEditor:
+ return SpatialBuildPlatforms.OSX;
+ case RuntimePlatform.LinuxEditor:
+ return SpatialBuildPlatforms.Linux;
+ default:
+ throw new Exception("Unsupported platform detected: " + Application.platform);
+ }
+ }
+
+ public static void Clean()
+ {
+ FileUtil.DeleteFileOrDirectory(PlayerBuildDirectory);
+ FileUtil.DeleteFileOrDirectory(BuildPaths.BuildScratchDirectory);
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/WorkerBuilder.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/WorkerBuilder.cs.meta
new file mode 100644
index 0000000..5047ee9
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Editor/WorkerBuilder.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: b5b3752654112eb4e99f5ac2f71db12d
+timeCreated: 1513951010
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime.meta
new file mode 100644
index 0000000..7422b42
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: b3facd901a528c14283ce8dce6655787
+folderAsset: yes
+timeCreated: 1515593925
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/BasicTemplateProvider.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/BasicTemplateProvider.cs
new file mode 100644
index 0000000..b588359
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/BasicTemplateProvider.cs
@@ -0,0 +1,75 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using Improbable.Assets;
+using Improbable.Unity.Assets;
+using Improbable.Unity.Configuration;
+using Improbable.Unity.Core;
+using Improbable.Unity.Entity;
+using UnityEngine;
+
+namespace Improbable.MinimalBuildSystem.Prefabs
+{
+ public class BasicTemplateProvider : MonoBehaviour, IEntityTemplateProvider
+ {
+ // These can be overridden on the command line.
+ public bool UseLocalPrefabs = true;
+
+ // The template provider can't be instantiated during construction as Application.isEditor doesn't work.
+ private IEntityTemplateProvider templateProvider;
+
+ private IEntityTemplateProvider TemplateProvider
+ {
+ get
+ {
+ if (templateProvider == null)
+ {
+ var gameObjectLoader = InitializeAssetLoader();
+ templateProvider = InitializeTemplateProvider(gameObjectLoader);
+ }
+
+ return templateProvider;
+ }
+ }
+
+ private IAssetLoader InitializeAssetLoader()
+ {
+#if UNITY_EDITOR
+ UseLocalPrefabs =
+ SpatialOS.Configuration.GetCommandLineValue(CommandLineConfigNames.UseLocalPrefabs, UseLocalPrefabs);
+ if (UseLocalPrefabs)
+ {
+ return new PrefabGameObjectLoader();
+ }
+#endif
+ return new ResourceGameObjectLoader();
+ }
+
+ private IEntityTemplateProvider InitializeTemplateProvider(IAssetLoader gameObjectLoader)
+ {
+#if UNITY_EDITOR
+ if (UseLocalPrefabs)
+ {
+ return new AssetDatabaseTemplateProvider(
+ new CachingAssetDatabase(new PreprocessingGameObjectLoader(gameObjectLoader)));
+ }
+#endif
+ return new AssetDatabaseTemplateProvider(new CachingAssetDatabase(gameObjectLoader));
+ }
+
+ public void PrepareTemplate(string prefabName, Action onSuccess, Action onError)
+ {
+ TemplateProvider.PrepareTemplate(prefabName, onSuccess, onError);
+ }
+
+ public GameObject GetEntityTemplate(string prefabName)
+ {
+ return TemplateProvider.GetEntityTemplate(prefabName);
+ }
+
+ public void CancelAllTemplatePreparations()
+ {
+ TemplateProvider.CancelAllTemplatePreparations();
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/BasicTemplateProvider.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/BasicTemplateProvider.cs.meta
new file mode 100644
index 0000000..39680f1
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/BasicTemplateProvider.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 6210cd2fa437e0e45a8867eaffb36e9c
+timeCreated: 1513334970
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/ResourceGameObjectLoader.cs b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/ResourceGameObjectLoader.cs
new file mode 100644
index 0000000..54e89c6
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/ResourceGameObjectLoader.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using Improbable.Assets;
+using UnityEngine;
+
+namespace Improbable.MinimalBuildSystem.Prefabs
+{
+ public class ResourceGameObjectLoader : IAssetLoader
+ {
+ public void LoadAsset(string prefabName, Action onAssetLoaded, Action onError)
+ {
+ var gameObject = Resources.Load("EntityPrefabs/" + prefabName);
+ if (gameObject == null)
+ {
+ onError(new Exception("Prefab not found: " + prefabName));
+ }
+ else
+ {
+ onAssetLoaded(gameObject);
+ }
+ }
+
+ public void CancelAllLoads()
+ {
+ // LoadAsset is instantaneous, so no need to do anything here.
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/ResourceGameObjectLoader.cs.meta b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/ResourceGameObjectLoader.cs.meta
new file mode 100644
index 0000000..4aa9fe9
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.Modules/MinimalBuildSystem/Runtime/ResourceGameObjectLoader.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: f614dc3930838624f97d4c36b63b4ca6
+timeCreated: 1513336271
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math.meta b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math.meta
new file mode 100644
index 0000000..79d5dc7
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 38fabc5c0ace6d040b77db18e18e54bc
+folderAsset: yes
+timeCreated: 1497005776
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/CoordinatesUnityPartial.cs b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/CoordinatesUnityPartial.cs
new file mode 100644
index 0000000..4c1c0b4
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/CoordinatesUnityPartial.cs
@@ -0,0 +1,184 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+
+using UnityEngine;
+
+namespace Improbable
+{
+ public partial struct Coordinates
+ {
+ ///
+ /// A Coordinates with 0 in each dimension.
+ ///
+ public static readonly Coordinates ZERO = new Coordinates(0, 0, 0);
+
+ public static Vector3d operator -(Coordinates v1, Coordinates v2)
+ {
+ return new Vector3d(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
+ }
+
+ public static Coordinates operator -(Coordinates v1, Vector3d v2)
+ {
+ return new Coordinates(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
+ }
+
+ public static Coordinates operator +(Coordinates a, Vector3d b)
+ {
+ return new Coordinates(a.x + b.x, a.y + b.y, a.z + b.z);
+ }
+
+ public static Coordinates operator -(Coordinates v1, Vector3f v2)
+ {
+ return new Coordinates(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
+ }
+
+ public static Coordinates operator +(Coordinates a, Vector3f b)
+ {
+ return new Coordinates(a.x + b.x, a.y + b.y, a.z + b.z);
+ }
+
+ public static Coordinates operator -(Coordinates v1, Vector3 v2)
+ {
+ return new Coordinates(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
+ }
+
+ public static Coordinates operator +(Coordinates a, Vector3 b)
+ {
+ return new Coordinates(a.x + b.x, a.y + b.y, a.z + b.z);
+ }
+
+ public static Coordinates operator -(Vector3d v1, Coordinates v2)
+ {
+ return new Coordinates(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
+ }
+
+ public static Coordinates operator -(Vector3f v1, Coordinates v2)
+ {
+ return new Coordinates(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
+ }
+
+ public static Coordinates operator -(Vector3 v1, Coordinates v2)
+ {
+ return new Coordinates(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
+ }
+
+ public static Coordinates operator +(Vector3d a, Coordinates b)
+ {
+ return new Coordinates(a.x + b.x, a.y + b.y, a.z + b.z);
+ }
+
+ public static Coordinates operator +(Vector3f a, Coordinates b)
+ {
+ return new Coordinates(a.x + b.x, a.y + b.y, a.z + b.z);
+ }
+
+ public static Coordinates operator +(Vector3 a, Coordinates b)
+ {
+ return new Coordinates(a.x + b.x, a.y + b.y, a.z + b.z);
+ }
+
+ ///
+ /// Interpolates between to global coordinates
+ ///
+ /// The starting position
+ /// The position to intepolate towards
+ ///
+ /// where 0 is the currentPosition and 1 is newPosition. Note: the value is clamped between 0
+ /// and 1 to prevent extrapolation.
+ ///
+ /// The interpolated position
+ public static Coordinates Lerp(Coordinates currentPosition, Coordinates newPosition, float progressRatio)
+ {
+ progressRatio = progressRatio > 1 ? 1 : (progressRatio < 0 ? 0 : progressRatio); // Clamp
+ var valueDelta = newPosition - currentPosition;
+ return valueDelta * progressRatio + currentPosition;
+ }
+
+ public double X
+ {
+ get { return x; }
+ set { x = value; }
+ }
+
+ public double Y
+ {
+ get { return y; }
+ set { y = value; }
+ }
+
+ public double Z
+ {
+ get { return z; }
+ set { z = value; }
+ }
+
+ ///
+ /// Check if a coordinate is near another.
+ ///
+ /// The coordinate to test.
+ /// The allowed range.
+ /// True if the other coordinate is within strictly less than the specified range.
+ public bool IsWithinDistance(Coordinates other, double distance)
+ {
+#pragma warning disable 618
+ return IsWithinSquareDistance(other, distance * distance);
+#pragma warning restore 618
+ }
+
+ ///
+ /// Calculate the square-space distance between two coordinates.
+ ///
+ /// The square-space distance between two coordinates.
+ public static double SquareDistance(Coordinates v1, Coordinates v2)
+ {
+ return (v1 - v2).SquareMagnitude();
+ }
+
+ ///
+ /// Check if a coordinate is near another.
+ ///
+ /// The coordinate to test.
+ /// The allowed square-space range.
+ /// True if the other coordinate is within strictly less than the specified range.
+ public bool IsWithinSquareDistance(Coordinates other, double sqrDistance)
+ {
+ return SquareDistance(this, other) < sqrDistance;
+ }
+
+ ///
+ /// True if all components of the Coordinates are real numbers.
+ ///
+ public bool IsFinite
+ {
+ get
+ {
+ return !double.IsNaN(x) && !double.IsNaN(y) && !double.IsNaN(z) &&
+ !double.IsInfinity(x) && !double.IsInfinity(y) && !double.IsInfinity(z);
+ }
+ }
+
+ ///
+ /// Converts to a Unity Vector3.
+ ///
+ public Vector3 ToUnityVector()
+ {
+ return new Vector3((float) x, (float) y, (float) z);
+ }
+
+ ///
+ /// Converts to a Spatial Vector3d.
+ ///
+ public Vector3d ToSpatialVector3d()
+ {
+ return new Vector3d(x, y, z);
+ }
+
+ ///
+ /// Returns the string representation of the Coordinates.
+ ///
+ public override string ToString()
+ {
+ return "Coordinates(" + x + ", " + y + ", " + z + ")";
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/CoordinatesUnityPartial.cs.meta b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/CoordinatesUnityPartial.cs.meta
new file mode 100644
index 0000000..0bc8ca7
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/CoordinatesUnityPartial.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: e6c7cfc31026aff44bf3f6d8c89b4861
+timeCreated: 1496998314
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3dUnityPartial.cs b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3dUnityPartial.cs
new file mode 100644
index 0000000..c74d814
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3dUnityPartial.cs
@@ -0,0 +1,123 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+
+using UnityEngine;
+
+namespace Improbable
+{
+ public partial struct Vector3d
+ {
+ ///
+ /// A Vector3d with 0 in each dimension.
+ ///
+ public static readonly Vector3d ZERO = new Vector3d(0, 0, 0);
+
+ ///
+ /// Override of the multiplication opporator. Used for multiplying the Vector3d by a float scalar.
+ ///
+ public static Vector3d operator *(Vector3d vector3d, double scalar)
+ {
+ return new Vector3d(vector3d.x * scalar, vector3d.y * scalar, vector3d.z * scalar);
+ }
+
+ ///
+ /// Override of the multiplication opporator. Used for multiplying the Vector3d by a float scalar.
+ ///
+ public static Vector3d operator *(double scalar, Vector3d vector3d)
+ {
+ return new Vector3d(vector3d.x * scalar, vector3d.y * scalar, vector3d.z * scalar);
+ }
+
+ ///
+ /// Override of the division opporator. Used for dividing the Vector3d by a float scalar.
+ ///
+ public static Vector3d operator /(Vector3d vector3d, double scalar)
+ {
+ return new Vector3d(vector3d.x / scalar, vector3d.y / scalar, vector3d.z / scalar);
+ }
+
+ ///
+ /// Override of the addition opporator. Used for adding two Vector3s.
+ ///
+ public static Vector3d operator +(Vector3d vector3d, Vector3d addvector3d)
+ {
+ return new Vector3d(vector3d.x + addvector3d.x, vector3d.y + addvector3d.y, vector3d.z + addvector3d.z);
+ }
+
+ ///
+ /// Override of the subtraction opporator. Used for subtracting one Vector3d from another.
+ ///
+ public static Vector3d operator -(Vector3d vector3d, Vector3d subtractVector3d)
+ {
+ return new Vector3d(vector3d.x - subtractVector3d.x, vector3d.y - subtractVector3d.y, vector3d.z - subtractVector3d.z);
+ }
+
+ ///
+ /// Computes the square of the magnitude of the Vector3d.
+ ///
+ public double SquareMagnitude()
+ {
+ return x * x + y * y + z * z;
+ }
+
+ ///
+ /// Returns the normal of the Vector3d (does not modify the original Vector3f).
+ ///
+ public Vector3d Normalized()
+ {
+ var magnitude = System.Math.Sqrt(SquareMagnitude());
+ return new Vector3d(x / magnitude, y / magnitude, z / magnitude);
+ }
+
+ public double X
+ {
+ get { return x; }
+ }
+
+ public double Y
+ {
+ get { return y; }
+ }
+
+ public double Z
+ {
+ get { return z; }
+ }
+
+ ///
+ /// True if all components of the Vector3d are real numbers.
+ ///
+ public bool IsFinite
+ {
+ get
+ {
+ return !double.IsNaN(x) && !double.IsNaN(y) && !double.IsNaN(z) &&
+ !double.IsInfinity(x) && !double.IsInfinity(y) && !double.IsInfinity(z);
+ }
+ }
+
+ ///
+ /// Converts the Vector3d to a Unity Vector3.
+ ///
+ public Vector3 ToUnityVector()
+ {
+ return new Vector3((float) x, (float) y, (float) z);
+ }
+
+ ///
+ /// Converts the Vector3d to a Unity Quaternion.
+ ///
+ public Quaternion ToUnityQuaternion()
+ {
+ return Quaternion.Euler(ToUnityVector());
+ }
+
+ ///
+ /// Returns the string representation of the Vector3d.
+ ///
+ public override string ToString()
+ {
+ return "Vector3d(" + x + ", " + y + ", " + z + ")";
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3dUnityPartial.cs.meta b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3dUnityPartial.cs.meta
new file mode 100644
index 0000000..395950f
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3dUnityPartial.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 192c9bb92bd23504fb1eeaab4df209ea
+timeCreated: 1496998314
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3fUnityPartial.cs b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3fUnityPartial.cs
new file mode 100644
index 0000000..e59e527
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3fUnityPartial.cs
@@ -0,0 +1,123 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+
+using UnityEngine;
+
+namespace Improbable
+{
+ public partial struct Vector3f
+ {
+ ///
+ /// A Vector3f with 0f in each dimension.
+ ///
+ public static readonly Vector3f ZERO = new Vector3f(0f, 0f, 0f);
+
+ ///
+ /// Override of the multiplication opporator. Used for multiplying the Vector3f by a float scalar.
+ ///
+ public static Vector3f operator *(Vector3f vector3f, float scalar)
+ {
+ return new Vector3f(vector3f.x * scalar, vector3f.y * scalar, vector3f.z * scalar);
+ }
+
+ ///
+ /// Override of the multiplication opporator. Used for multiplying the Vector3f by a float scalar.
+ ///
+ public static Vector3f operator *(float scalar, Vector3f vector3f)
+ {
+ return new Vector3f(vector3f.x * scalar, vector3f.y * scalar, vector3f.z * scalar);
+ }
+
+ ///
+ /// Override of the division opporator. Used for dividing the Vector3f by a float scalar.
+ ///
+ public static Vector3f operator /(Vector3f vector3f, float scalar)
+ {
+ return new Vector3f(vector3f.x / scalar, vector3f.y / scalar, vector3f.z / scalar);
+ }
+
+ ///
+ /// Override of the addition opporator. Used for adding two Vector3s.
+ ///
+ public static Vector3f operator +(Vector3f vector3f, Vector3f addVector3f)
+ {
+ return new Vector3f(vector3f.x + addVector3f.x, vector3f.y + addVector3f.y, vector3f.z + addVector3f.z);
+ }
+
+ ///
+ /// Override of the subtraction opporator. Used for subtracting one Vector3f from another.
+ ///
+ public static Vector3f operator -(Vector3f vector3f, Vector3f subtractVector3f)
+ {
+ return new Vector3f(vector3f.x - subtractVector3f.x, vector3f.y - subtractVector3f.y, vector3f.z - subtractVector3f.z);
+ }
+
+ ///
+ /// Computes the square of the magnitude of the Vector3f.
+ ///
+ public float SquareMagnitude()
+ {
+ return x * x + y * y + z * z;
+ }
+
+ ///
+ /// Returns the normal of the Vector3f (does not modify the original Vector3f).
+ ///
+ public Vector3f Normalized()
+ {
+ var magnitude = (float) System.Math.Sqrt(SquareMagnitude());
+ return new Vector3f(x / magnitude, y / magnitude, z / magnitude);
+ }
+
+ public float X
+ {
+ get { return x; }
+ }
+
+ public float Y
+ {
+ get { return y; }
+ }
+
+ public float Z
+ {
+ get { return z; }
+ }
+
+ ///
+ /// True if all components of the Vector3f are real numbers.
+ ///
+ public bool IsFinite
+ {
+ get
+ {
+ return !float.IsNaN(x) && !float.IsNaN(y) && !float.IsNaN(z) &&
+ !float.IsInfinity(x) && !float.IsInfinity(y) && !float.IsInfinity(z);
+ }
+ }
+
+ ///
+ /// Converts the Vector3f to a Unity Vector3.
+ ///
+ public Vector3 ToUnityVector()
+ {
+ return new Vector3(x, y, z);
+ }
+
+ ///
+ /// Converts the Vector3f to a Unity Quaternion.
+ ///
+ public Quaternion ToUnityQuaternion()
+ {
+ return Quaternion.Euler(ToUnityVector());
+ }
+
+ ///
+ /// Returns the string representation of the Vector3f.
+ ///
+ public override string ToString()
+ {
+ return "Vector3f(" + x + ", " + y + ", " + z + ")";
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3fUnityPartial.cs.meta b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3fUnityPartial.cs.meta
new file mode 100644
index 0000000..749d0eb
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/.UnityPartials/Math/Vector3fUnityPartial.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 81f000d9b57440e46bfde4f0dbc79879
+timeCreated: 1496998314
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core.meta b/workers/unity/Assets/Plugins/Improbable/Core.meta
new file mode 100644
index 0000000..aa2b0e9
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 8bb4021c6123b494e8d163021363b220
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Linux.meta b/workers/unity/Assets/Plugins/Improbable/Core/Linux.meta
new file mode 100644
index 0000000..147eaea
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Linux.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 2e778f30959d283429868b08dd5be1fc
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins.meta b/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins.meta
new file mode 100644
index 0000000..70afe06
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: baf20348710c14f4e96179ebf7b5b75b
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins/x86_64.meta b/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins/x86_64.meta
new file mode 100644
index 0000000..05bc9b2
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins/x86_64.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 673b9cc2e58656547b1b030bedecd15e
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins/x86_64/libCoreSdkDll.so b/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins/x86_64/libCoreSdkDll.so
new file mode 100644
index 0000000..2ecd388
Binary files /dev/null and b/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins/x86_64/libCoreSdkDll.so differ
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins/x86_64/libCoreSdkDll.so.meta b/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins/x86_64/libCoreSdkDll.so.meta
new file mode 100644
index 0000000..c52528d
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Linux/Plugins/x86_64/libCoreSdkDll.so.meta
@@ -0,0 +1,87 @@
+fileFormatVersion: 2
+guid: c7da83f6aa260a7488cc31842380f697
+timeCreated: 1526049940
+licenseType: Pro
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ isPreloaded: 0
+ isOverridable: 0
+ platformData:
+ - first:
+ '': OSXIntel
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ '': OSXIntel64
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ CPU: x86_64
+ DefaultValueInitialized: true
+ - first:
+ Facebook: Win
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ Facebook: Win64
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Linux
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ Standalone: Linux64
+ second:
+ enabled: 1
+ settings:
+ CPU: x86_64
+ - first:
+ Standalone: LinuxUniversal
+ second:
+ enabled: 1
+ settings:
+ CPU: x86_64
+ - first:
+ Standalone: OSXUniversal
+ second:
+ enabled: 0
+ settings:
+ CPU: x86_64
+ - first:
+ Standalone: Win
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ Standalone: Win64
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/OSX.meta b/workers/unity/Assets/Plugins/Improbable/Core/OSX.meta
new file mode 100644
index 0000000..228a70a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/OSX.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: d1031e26f23afed43bb3ad7feef58f92
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle.meta b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle.meta
new file mode 100644
index 0000000..2c709c2
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle.meta
@@ -0,0 +1,42 @@
+fileFormatVersion: 2
+guid: beed007e08ec5e74b9ce017361061751
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ isPreloaded: 0
+ isOverridable: 0
+ platformData:
+ - first:
+ '': OSXIntel
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ '': OSXIntel64
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 1
+ settings:
+ DefaultValueInitialized: true
+ - first:
+ Standalone: OSXUniversal
+ second:
+ enabled: 1
+ settings: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents.meta b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents.meta
new file mode 100644
index 0000000..f91e993
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 423ed5cbd20219349bf143381ae64f99
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/Info.plist b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/Info.plist
new file mode 100644
index 0000000..0aab70e
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/Info.plist
@@ -0,0 +1,38 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ CoreSdkDll
+ CFBundleGetInfoString
+
+ CFBundleIconFile
+
+ CFBundleIdentifier
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleLongVersionString
+
+ CFBundleName
+
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+
+ CFBundleSignature
+ ????
+ CFBundleVersion
+
+ CSResourcesFileMapped
+
+ NSHumanReadableCopyright
+
+ NSPrincipalClass
+ NSApplication
+ NSHighResolutionCapable
+ True
+
+
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/Info.plist.meta b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/Info.plist.meta
new file mode 100644
index 0000000..c301877
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/Info.plist.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 79ff8ad7eefbb8c4988509d091dcc37e
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/MacOS.meta b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/MacOS.meta
new file mode 100644
index 0000000..9a4eee2
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/MacOS.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: bca08ae668e2354458867858c8ef86cb
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/MacOS/CoreSdkDll b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/MacOS/CoreSdkDll
new file mode 100644
index 0000000..83c4350
Binary files /dev/null and b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/MacOS/CoreSdkDll differ
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/MacOS/CoreSdkDll.meta b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/MacOS/CoreSdkDll.meta
new file mode 100644
index 0000000..fff3fd3
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/OSX/CoreSdkDll.bundle/Contents/MacOS/CoreSdkDll.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 47880e36d5d6c63489f1cd2f41154136
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows.meta b/workers/unity/Assets/Plugins/Improbable/Core/Windows.meta
new file mode 100644
index 0000000..7eaf844
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Windows.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: aa0c0ab22821aa4469237eecd83f459d
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins.meta b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins.meta
new file mode 100644
index 0000000..72b7265
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 6232c8ec78b8314408b1a9e89cf3daa9
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86.meta b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86.meta
new file mode 100644
index 0000000..15ced74
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 0695e9ee3bdcf154ea1805742482d347
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.dll b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.dll
new file mode 100644
index 0000000..4d3e379
Binary files /dev/null and b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.dll differ
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.dll.meta b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.dll.meta
new file mode 100644
index 0000000..8820086
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.dll.meta
@@ -0,0 +1,87 @@
+fileFormatVersion: 2
+guid: de819717a5c44914e95e9e8f5c474fd3
+timeCreated: 1526049940
+licenseType: Pro
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ isPreloaded: 0
+ isOverridable: 0
+ platformData:
+ - first:
+ '': OSXIntel
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ - first:
+ '': OSXIntel64
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ CPU: x86
+ DefaultValueInitialized: true
+ - first:
+ Facebook: Win
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ - first:
+ Facebook: Win64
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ Standalone: Linux
+ second:
+ enabled: 1
+ settings:
+ CPU: x86
+ - first:
+ Standalone: Linux64
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ Standalone: LinuxUniversal
+ second:
+ enabled: 1
+ settings:
+ CPU: x86
+ - first:
+ Standalone: OSXUniversal
+ second:
+ enabled: 0
+ settings:
+ CPU: x86
+ - first:
+ Standalone: Win
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Win64
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.lib b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.lib
new file mode 100644
index 0000000..6cb6580
Binary files /dev/null and b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.lib differ
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.lib.meta b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.lib.meta
new file mode 100644
index 0000000..a0ae1d4
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86/CoreSdkDll.lib.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 9e3f9addf9eb37b40bcdec98ed959e8b
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64.meta b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64.meta
new file mode 100644
index 0000000..2ab55b4
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 3888a8c27eedc3245966f839caeac8ba
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.dll b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.dll
new file mode 100644
index 0000000..25cead0
Binary files /dev/null and b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.dll differ
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.dll.meta b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.dll.meta
new file mode 100644
index 0000000..e45eeff
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.dll.meta
@@ -0,0 +1,87 @@
+fileFormatVersion: 2
+guid: 3b3671b8866b82e44bc3c73c32c9c623
+timeCreated: 1526049940
+licenseType: Pro
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ isPreloaded: 0
+ isOverridable: 0
+ platformData:
+ - first:
+ '': OSXIntel
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ '': OSXIntel64
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ CPU: x86_64
+ DefaultValueInitialized: true
+ - first:
+ Facebook: Win
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ Facebook: Win64
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Linux
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ Standalone: Linux64
+ second:
+ enabled: 1
+ settings:
+ CPU: x86_64
+ - first:
+ Standalone: LinuxUniversal
+ second:
+ enabled: 1
+ settings:
+ CPU: x86_64
+ - first:
+ Standalone: OSXUniversal
+ second:
+ enabled: 0
+ settings:
+ CPU: x86_64
+ - first:
+ Standalone: Win
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ Standalone: Win64
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.lib b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.lib
new file mode 100644
index 0000000..896d6f4
Binary files /dev/null and b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.lib differ
diff --git a/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.lib.meta b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.lib.meta
new file mode 100644
index 0000000..5652b21
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Core/Windows/Plugins/x86_64/CoreSdkDll.lib.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: cd065eb1d5e6f134f8d37dcc3bc75c3b
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Editor.meta b/workers/unity/Assets/Plugins/Improbable/Editor.meta
new file mode 100644
index 0000000..5b64c85
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Editor.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 01d4e8251b7728f4db095d688dd7446f
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Generated.meta b/workers/unity/Assets/Plugins/Improbable/Generated.meta
new file mode 100644
index 0000000..3ded760
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Generated.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: acaae33012c48e44c91c9f1f2dbe5ead
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk.meta b/workers/unity/Assets/Plugins/Improbable/Sdk.meta
new file mode 100644
index 0000000..a516d6a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 5f2c987cbd973ca4b863014c0be23ff5
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Dll.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll.meta
new file mode 100644
index 0000000..7244e44
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 26332946ed4bba54ba8da78b86cffde5
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.dll b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.dll
new file mode 100644
index 0000000..dba0954
Binary files /dev/null and b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.dll differ
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.dll.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.dll.meta
new file mode 100644
index 0000000..8176660
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.dll.meta
@@ -0,0 +1,32 @@
+fileFormatVersion: 2
+guid: e2945882351da4f98b2442bfa5fa02f6
+timeCreated: 1525947628
+licenseType: Pro
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ isPreloaded: 0
+ isOverridable: 0
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ - first:
+ Windows Store Apps: WindowsStoreApps
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.xml b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.xml
new file mode 100644
index 0000000..58847e6
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.xml
@@ -0,0 +1,440 @@
+
+
+
+ Improbable.WorkerSdkCsharp.Framework
+
+
+
+
+ Object for sending asynchronous responses to command requests.
+ Upon receiving an incoming command request, a user-defined callback containing a user-submitted strategy for responding to the request is invoked.
+
+
+
+
+ Type definition: user defined callback function returning a command response given a command request.
+
+
+
+
+ Constructor method that sets the callback field.
+
+
+
+
+ Respond to an incoming command request asynchronously by invoking the user-defined callback.
+ The logic defined in the callback needs to eventually lead to an invokation of responseHandle.Respond().
+
+
+
+
+ Interface abstracting evidence class for a command on a component.
+
+ Command metaclass
+ Payload of the Command
+ Return type of the Command
+
+
+
+ Creates an ICommandRequest object appropriate for the command's request type.
+
+
+
+
+ Creates an ICommandResponse object appropriate for the command's response type.
+
+
+
+
+ Extracts the command response from ICommandResponse object.
+
+
+
+
+ Extracts the command response from ICommandResponse object.
+
+
+
+
+ Internal name of the command to be invoked.
+
+
+
+
+ Evidence class for a command on a component.
+
+
+ Payload of the Command
+ Return type of the Command
+
+
+
+ ComponentId of the component the command should be invoked on.
+
+
+
+
+ Internal name of the command to be invoked.
+
+
+
+
+ Creates an ICommandRequest object appropriate for the command's request type.
+
+
+
+
+ Creates an ICommandResponse object appropriate for the command's response type.
+
+
+
+
+ Extracts the command request from ICommandResponse object.
+
+
+
+
+ Extracts the command response from ICommandResponse object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Marks an interface as the Reader Interface for use by visualizers.
+
+
+
+
+ Marks an interface as the Writer Interface for use by visualizers.
+
+
+
+
+ Associates a unique component Id with a specific interface.
+
+
+
+
+ The unique identifier.
+
+
+
+
+ Creates an instance of the attribute.
+
+
+
+
+ Metadata for the incoming command request.
+
+
+
+
+ Creates an instance of CommandRequestMetadata.
+
+
+
+
+
+
+
+
+
+
+ Contains callbacks that are invoked in response to events registered by calling
+ IComponentFactory.RegisterWithConnection().
+
+
+
+
+ Contains an object with no callbacks specified.
+
+
+
+
+ Invoked after a component has been added.
+
+
+ Provides the EntityId the component belongs to, the metaclass of the component,
+ and the concrete implementation of the component.
+
+
+
+
+ Invoked after a component has been removed.
+
+
+ Provides the EntityId the component belongs to, the metaclass of the component,
+ and the concrete implementation of the component.
+
+
+
+
+ Invoked after authority over the component has been granted or revoked.
+
+
+ Provides the EntityId the component belongs to, the metaclass of the component,
+ whether or authority is granted (true) or revoked (false), and the concrete
+ implementation of the component.
+
+
+
+
+ Invoked after the component has been updated.
+
+
+ Provides the EntityId the component belongs to, the meta class of the component,
+ and the concrete implementation of the component.
+
+
+
+
+ Handles callbacks for events.
+
+
+
+
+
+
+
+
+
+
+
+ Invokes callbacks with every member of a given object list.
+
+
+
+
+
+ Invokes callbacks with a given data item.
+
+
+
+
+
+ Object holding an ICommandResponder.
+ Exposed to the user by calling [component name].Writer.On[command name].
+ Defines the user facing interface for registering/deregistering synchronous/asynchronous command response strategies.
+
+
+
+
+ Register a user-defined synchronous callback for responding to an incoming command request.
+ A CommandResponderWrapper can only have one ICommandResponder registered at the same time.
+
+
+ If a response is already registered.
+
+
+
+
+ Register a user-defined asynchronous callback for responding to an incoming command request.
+ A CommandResponderWrapper can only have one ICommandResponder registered at the same time.
+
+
+ If a response is already registered.
+
+
+
+
+ De-register a user-defined callback for responding to an incoming command request.
+
+
+ If no response is registered.
+
+
+
+
+ Invokes the commandResponder member fields SendResponse() method upon receiving a command request to send a response.
+
+
+
+
+ Interface type for objects that respond to incoming command requests given a user-defined callback.
+
+
+
+
+ Send a response to an incoming command request given a ResponseHandle corresponding to said request.
+
+
+
+
+ Handles callbacks for events.
+
+
+
+
+
+ Registers an event callback.
+
+
+
+
+ Removes a registered event callback.
+
+
+
+
+ Handles user-registered callbacks.
+
+
+
+
+
+ Creates an instance of UserCallbackHandler.
+
+
+
+
+
+
+
+
+ This class provides a stable, reference-counted memoiser. While the reference count for a particular
+ computation is > 0, the returned results of Add and Remove will return the same object as a result.
+ As the reference count is explicit, each call to Add must be matched with a call to Remove. Not calling
+ Remove will cause the class to not clean up its references. Calling Remove without an Add results in an error.
+
+
+
+
+ Metadata for the incoming command request.
+
+
+
+
+ WorkerId of the caller.
+
+
+
+
+ Attribute set of the caller.
+
+
+
+
+ An interface that allows for component meta classes to receive events
+ on a per-entity basis.
+
+
+
+
+ Call this once for each associated meta class to receive callbacks.
+ These callbacks will be called for the lifetime of the dispatcher object.
+
+
+
+
+ Call this once for each associated meta class when the connection is disposed of.
+
+
+
+
+ Returns an instance of the derived metaclass associated with the entity.
+
+
+
+
+ This method no longer does anything and should not be used.
+ Removes all event handlers from the instance associated with entityId.
+
+
+
+
+ An interface for writers, allowing identification of them as an (entity, component) pair.
+
+
+
+
+ The unique component Id.
+
+
+
+
+ The entityId associated with the writer instance.
+
+
+
+
+ Handles property changed callbacks.
+
+
+
+
+
+ Registers a property callback.
+
+
+
+
+ Registers a property callback and invokes it immediately with the current value.
+
+
+
+
+
+ Removes a registered property callback.
+
+
+
+
+ Object for sending synchronous responses to command requests.
+ Upon receiving an incoming command request, a response is composed with the help of a user-defined callback and sent back to the requester immediately.
+
+
+
+
+ Type definition: user defined callback function returning a command response given a command request.
+
+
+
+
+ Constructor method that sets the callback field.
+
+
+
+
+ Respond to an incoming command request synchronously by sending back a command response immediately.
+
+
+
+
+ Object passed to objects handling the command requests.
+
+
+
+
+ Payload sent by the entity sending the command.
+
+
+
+
+ Metadata for this command invocation instance.
+
+
+
+
+ Creates a ResponseHandle. Called from generated code.
+
+
+
+
+ Sends the response to command request.
+
+
+
+
+
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.xml.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.xml.meta
new file mode 100644
index 0000000..a2c05a2
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.Framework.xml.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 4abc765c01cbd4d3a9d44dfa4d32a22e
+timeCreated: 1525947639
+licenseType: Pro
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.dll b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.dll
new file mode 100644
index 0000000..04eb516
Binary files /dev/null and b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.dll differ
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.dll.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.dll.meta
new file mode 100644
index 0000000..75ff248
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.dll.meta
@@ -0,0 +1,32 @@
+fileFormatVersion: 2
+guid: 75459aafa1dd9482c88d918a1b2ecac0
+timeCreated: 1525947627
+licenseType: Pro
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ isPreloaded: 0
+ isOverridable: 0
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ - first:
+ Windows Store Apps: WindowsStoreApps
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.xml b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.xml
new file mode 100644
index 0000000..8667646
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.xml
@@ -0,0 +1,1652 @@
+
+
+
+ Improbable.WorkerSdkCsharp
+
+
+
+
+ A unique identifier used to look up entity in SpatialOS.
+
+
+ Instances of this type should be treated as a transient identifiers that will not be
+ consistent between different runs of the same simulation.
+
+
+
+
+ An invalid entity ID. Note that this just one of many invalid entity IDs.
+
+
+
+
+
+ The value of the EntityId.
+
+
+ Though this value is numeric, you should not perform any mathematical operations on it.
+
+
+
+
+ Constructs a new instance of an EntityId.
+
+
+
+
+ Whether this represents a valid SpatialOS entity ID. Specifically, Id > 0
.
+
+ True iff valid.
+
+
+
+ Whether the specified entity ID is invalid.
+
+
+ An entity ID.
+ True iff entityId is invalid
+
+
+
+ Whether the specified entity ID is valid.
+
+
+ An entity ID.
+ True iff entityId is valid
+
+
+
+
+
+
+
+
+
+ Returns true if entityId1 is exactly equal to entityId2.
+
+
+
+
+ Returns true if entityId1 is not exactly equal to entityId2.
+
+
+
+
+
+
+
+
+
+
+ This class maintains a per-type cache of DeepCopier objects for efficiency.
+ This helps to avoid performing multiple runtime type checks and keeps the
+ logic of creating DeepCopier objects all in the same place.
+
+
+
+
+ This class is the same as System.Collections.Generic.List, except that it is
+ augmented with proper structural equality and GetHashCode.
+
+
+
+
+ Returns a deep copy of this list. Checks if the types in the list are either
+ primitive or implement the IDeepCopyable interface, then performs a
+ deep copy of the list if they are and throws an invalid operation exception
+ otherwise. If the list is empty it will return a new list.
+
+
+
+
+ Very similar to the usual C# Dictionary. However: iteration order is guaranteed to be
+ identical to insertion order; the dictionary can be traversed without allocation using
+ the First and Last properties; and it properly implements structural
+ equality and GetHashCode.
+
+
+
+
+ Returns a deep copy of this map. Checks if the types in the map are either
+ primitive or implement the IDeepCopyable interface, then performs a
+ deep copy of the map if they are and throws an invalid operation exception
+ otherwise. If the map is empty it will return a new map.
+
+
+
+
+ Represents an optional value. Works with both value types (in which case it behaves
+ similarly to a nullable type) and non-value types. Only allocates if a value is present.
+
+
+
+
+ Creates an Option containing the given value.
+
+
+
+
+ Implicit conversion from a value to an Option containing that value.
+
+
+
+
+ Implicit conversion from null to an empty Option (for value types).
+
+
+
+
+ Returns a deep copy of this option. Checks if the type in the option is either
+ primitive or implements the IDeepCopyable interface, then performs a
+ deep copy of the option if it is and throws an invalid operation exception
+ otherwise. If the option is empty it will return a new option.
+
+
+
+
+ Returns 1 if this Option contains a value, and 0 otherwise.
+
+
+
+
+ Removes the value from this Option, if present.
+
+
+
+
+ Assigns the value contained in this Option, replacing the existing value (if
+ present).
+
+
+
+
+ Returns true and assigns to value if this Option contains a value;
+ returns false otherwise.
+
+
+
+
+ Returns true if this Option contains a value, and false
+ otherwise.
+
+
+
+
+ Returns the value stored in this Option. Throws InvalidOperationException
+ if no value is present.
+
+
+
+
+ A wrapper for a byte array, with structural equality and hash code.
+
+
+ This is used by generated schema types to represent bytes
fields.
+
+
+
+
+ Creates a new Bytes
object wrapping a copy of the given byte array.
+
+
+
+
+ Creates a new Bytes
object wrapping a copy of the given native byte buffer.
+
+
+
+
+ Creates a new Bytes
object directly wrapping the given byte array.
+
+
+ Warning: since this method does not copy the array, modifications made to the source
+ array will be reflected by this Bytes
object. In particular, it is illegal
+ to modify the source array concurrently with other operations on this object.
+
+
+
+
+ Returns a copy of the byte array wrapped by this Bytes
object.
+
+
+
+
+ Directly returns the backing byte array wrapped by this Bytes
object.
+
+
+ Warning: since this method does not copy the array, modifications made to the returned
+ array will be reflected by this Bytes
object. In particular, it is illegal
+ to modify the source array concurrently with other operations on this object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Exposes a global hook to be invoked when errors are detected in client-side code. This is
+ generally used to indicate programmer error, and covers cases such as attempting to throw
+ an exception from a Dispatcher callback.
+
+
+
+
+ Sets the Action to be invoked when a client exception is detected.
+
+
+
+
+ Invokes the global client error hook to handle an exception.
+
+
+
+
+ Interface representing a particular component. The generated code will contain a metaclass
+ for each component in the schema. These metaclasses are used to identify components; in
+ particular they are given as the generic type parameter to various component-related classes
+ and methods.
+
+
+
+
+ The ID of this component.
+
+
+
+
+ This is an implementation detail provided by generated code.
+
+
+ Clients should not need to call this method.
+
+
+
+
+ This is an implementation detail provided by generated code.
+
+
+ Clients should not need to call this method.
+
+
+
+
+ Represents data-at-rest for the component identified by the metaclass C. Each component data
+ has an extension method Get() that returns the concrete data type.
+
+
+
+
+ Converts the at-rest data type to an update.
+
+
+
+
+ Represents an update for the component identified by the metaclass C. Each component update
+ has an extension method Get() that returns the concrete update type.
+
+
+
+
+ Converts the update to an at-rest data type. Calling this method is only valid if the
+ update provides a value for each field in the component; otherwise an exception is
+ thrown.
+
+
+
+
+ Applies this update to the corresponding at-rest data type for the component.
+
+
+
+
+ Interface representing a command for a particular component. The generated code for a
+ component will contain a command metaclass for each command defined by the component.
+ These metaclasses are used to identify commands; in particular they are given as the generic
+ type parameter to various command-related classes and methods.
+
+
+
+
+ Represents a request for the command identified by the metaclass C. Each command request has
+ an extension method Get() that returns the concrete request type.
+
+
+
+
+ This is an implementation detail provided by generated code.
+
+
+ Clients should not need to call this method.
+
+
+
+
+ Represents a response for the command identified by the metaclass C. Each command response
+ has an extension method Get() that returns the concrete response type.
+
+
+
+
+ This is an implementation detail provided by generated code.
+
+
+ Clients should not need to call this method.
+
+
+
+
+ An opaque list of operations retrieved from Connection::GetOpList(). It is usually passed to
+ Dispatcher::Process(), which dispatches the operations to the appropriate callbacks.
+
+
+
+
+
+
+
+ Overrides the default interest settings for a particular entity and component.
+
+
+
+ Controls whether checkout is explicitly enabled or disabled.
+
+
+
+ Parameters used to alter the behaviour of a command request.
+
+
+
+
+ Allow command requests to bypass the bridge when this worker is authoritative over the target
+ entity-component.
+
+
+
+
+ Worker Connection API. This is the main way of connecting to SpatialOS, processing
+ operations, and sending component updates.
+
+
+ This object should not be used concurrently by multiple threads.
+
+
+
+
+ Connects to a SpatialOS deployment via a receptionist. This is the flow used to connect
+ a managed worker running in the cloud alongside the deployment, and also to connect any
+ local worker to a (local or remote) deployment via a locally-running receptionist.
+
+
+ The hostname and port would typically be provided by SpatialOS on the command-line, if
+ this is a managed worker on the cloud, or otherwise be predetermined (e.g.
+ localhost:7777 for the default receptionist of a locally-running deployment).
+
+
+
+
+
+
+
+ Returns true if the Connection object was created correctly and has successfully
+ connected to SpatialOS.
+
+
+
+
+ Retrieves the list of operations that have occurred since the last call to this
+ function.
+
+
+ If timeoutMillis is non-zero, the function will block until there is at least one
+ operation to return, or the timeout has been exceeded. If the timeout is exceeded, an
+ empty list will be returned. If timeoutMillis is zero the function is non-blocking.
+
+
+
+
+ Returns the ID that was assigned to this worker at runtime.
+
+
+
+
+ Returns the attributes associated with this worker at runtime.
+
+
+
+
+ Sends a log message for the worker to SpatialOS.
+
+
+
+
+ Sends a set of metrics for the worker to SpatialOS. Typically this function should be
+ called periodically (e.g. once every second) to report the worker's status. Since
+ histogram metrics are diff-based, calling this function clears each histogram in the
+ Metrics parameter.
+
+
+
+
+ Requests SpatialOS to reserve an entity ID. Returns a request ID, which can be used to identify a
+ response to the request via the Dispatcher.OnReserveEntityIdResponse callback.
+
+
+ If timeout_millis is not specified, the default timeout will be used.
+
+
+
+
+ Requests SpatialOS to reserve a batch of entity IDs. Returns a request ID, which can be
+ used to identify a response to the request via the Dispatcher.OnReserveEntityIdResponse
+ callback.
+
+
+ If timeout_millis is not specified, the default timeout will be used.
+
+
+
+
+ Requests SpatialOS to create an entity. Returns a request ID, which can be used to identify
+ response to the request via the Dispatcher.OnCreateEntityResponse callback.
+
+
+ If an entity ID is provided, it must have been reserved using SendReserveEntityIdRequest().
+ If timeout_millis is not specified, the default timeout will be used.
+
+
+
+
+ Requests SpatialOS to delete an entity. Returns a request ID, which can be used to
+ identify a response to the request via the Dispatcher.OnDeleteEntityResponse callback.
+
+
+ If timeout_millis is not specified, the default timeout will be used.
+
+
+
+
+ Queries SpatialOS for remote entity data. Returns a request Id, which can be used to
+ identify a response to the request via the Dispatcher.OnEntityQueryResponse callback.
+
+
+ If timeout_millis is not specified, the default timeout will be used.
+
+
+
+
+ Sends a component interest update for the given entity to SpatialOS. By default, the
+ worker receives data for all entities according to the default component interests
+ specified in its bridge settings. This function overrides the default to explicitly
+ add or remove interest for particular components.
+
+
+ Interest for components not present in the interestOverrides map is unaffected. Note
+ also that components over which the worker is authoritative are always received,
+ regardless of interest settings.
+
+
+
+
+ Sends an acknowledgement of the receipt of an AuthorityLossImminent authority change for a
+ component. Sending the acknowledgement signifies that this worker is ready to lose authority
+ over the component.
+
+
+
+
+ Sends an update for an entity's component to SpatialOS. Note that the sent component
+ update is added as an operation to the operation list and will be returned by a
+ subsequent call to GetOpList().
+
+
+ The behaviour is undefined if the update is mutated after it is sent; use
+ SendComponentUpdate(update.DeepCopy()) if you intend to hold on to the update and
+ modify it later. The legacyCallbackSemantics parameter is deprecated and exists for
+ legacy compatibility only. It will be removed soon and should not be used.
+
+
+
+
+ Sends a command request to a component on a specific target entity. Returns a request
+ ID which can be used to identify a response to the command via the
+ Dispatcher.OnCommandResponse callback.
+
+
+ If timeoutMillis is not specified, the default timeout will be used. Like
+ SendComponentUpdate, the behaviour is undefined if the request is mutated after it is
+ sent; SendCommandRequest(request.DeepCopy()) if you intend to modify the object
+ later.
+
+
+
+
+ Sends a response to an incoming command request for a component on an entity over which
+ this worker has authority. The request ID should match an incoming command request via
+ the Dispatcher.OnCommandRequest callback.
+
+
+ Like SendComponentUpdate, the behaviour is undefined if the response is mutated after it
+ is sent; SendCommandResponse(response.DeepCopy()) if you intend to modify the
+ object later.
+
+
+
+
+ Explicitly fails an incoming command request for a component on an entity over which
+ this worker has authority. The request ID should match an incoming command request via
+ the Dispatcher.OnCommandRequest callback.
+
+
+ The calling worker will receive a command response with status code
+ StatusCode.ApplicationError.
+
+
+
+
+ Enables or disables protocol logging. Logging uses the parameters specified when the Connection
+ was created. Enabling it when already enabled, or disabling it when already disabled, do
+ nothing.
+
+
+ Note that logs from any previous protocol logging sessions will be overwritten.
+
+
+
+ The request was successfully executed and returned a response.
+
+
+
+ The request timed out before a response was received. It can be retried, but carefully -
+ this usually means the deployment is overloaded, so some sort of backoff should be used
+ to avoid making the problem worse. This can also be caused by the target worker's
+ handling code failing to respond to the command at all, perhaps due to a bug in its
+ implementation.
+
+
+
+
+ The target entity did not exist, or did not have the target component. This probably
+ means the entity either hasn't been created yet or has already been deleted. It might
+ make sense to retry the request if there is reason to believe the entity hasn't yet been
+ created but will be soon.
+
+
+
+
+ The request could not be executed by a worker, either because it lost authority while
+ handling the request, or because no worker was authoritative at all. Assuming the
+ deployment isn't irrecoverably broken (e.g. due to misconfigured loadbalancing or
+ crash-looping workers) this is a transient failure and can be retried immediately.
+
+
+
+
+ The worker did not have the required permissions to make the request. Permissions do
+ not change at runtime, so it doesn't make sense to retry the request.
+
+
+
+
+ The command was delivered successfully, but the handler rejected it. Either the command
+ was delivered to a worker that explicitly rejected it by calling
+ Connection.SendCommandFailure, or the request data was rejected as invalid by SpatialOS
+ itself. In the latter case, in particular, Connection.SendCreateEntityRequest will
+ return ApplicationError if an entity ID reservation has expired, and
+ Connection.SendEntityQueryRequest will return ApplicationError if the result set is
+ incomplete.
+
+
+
+
+ Some other error occurred. This likely indicates a bug in SpatialOS and should be
+ reported.
+
+
+
+
+ The authority state of an entity-component.
+
+
+
+
+ Data for an operation that indicates the Connection has disconnected and can no longer be
+ used.
+
+
+
+
+ Data for an operation that indicates that a worker flag has been updated
+
+
+
+
+ Data for an operation that provides a log message from the SDK.
+
+
+
+
+ Data for an operation that provides a report on built-in metrics from the SDK.
+
+
+
+
+ Data for an operation that provides a report on built-in metrics from the SDK.
+
+
+
+
+ Data for an operation that indicates an entity has been added to the worker's view of the
+ simulation.
+
+
+
+
+ Data for an operation that indicates an entity has been removed from the worker's view of
+ the simulation.
+
+
+
+
+ A response indicating the result of the entity ID reservation request.
+
+
+
+
+ The outgoing request ID for which there was a response. Matches the request ID returned
+ by a previous call to Connection.SendReserveEntityIdRequest.
+
+
+
+ The status code of the command response.
+
+
+ The error message.
+
+
+
+ If successful, a newly allocated entity id which is guaranteed to be unused in the current
+ deployment.
+
+
+
+
+ A response indicating the result of the multiple entity ID reservation request.
+
+
+
+
+ The outgoing request ID for which there was a response. Matches the request ID returned
+ by a previous call to Connection.SendReserveEntityIdRequest.
+
+
+
+ The status code of the command response.
+
+
+ The error message.
+
+
+
+ If successful, an ID which is the first in a contiguous range of newly allocated entity
+ IDs which are guaranteed to be unused in the current deployment.
+
+
+
+
+ If successful, the number of IDs reserved in the contiguous range, otherwise 0.
+
+
+
+
+ A response indicating the result of an entity creation request.
+
+
+
+
+ The outgoing request ID for which there was a response. Matches the request ID returned
+ by a previous call to Connection.SendCreateEntityRequest.
+
+
+
+ The status code of the command response.
+
+ If the status code is StatusCode.ApplicationError, the entity ID reservation has
+ expired and must be retried.
+
+
+
+ The error message.
+
+
+ If successful, the entity ID of the newly created entity.
+
+
+
+ A response indicating the result of an entity deletion request.
+
+
+
+
+ The outgoing request ID for which there was a response. Matches the request ID returned
+ by a previous call to Connection.SendDeleteEntityRequest.
+
+
+
+ The ID of the target entity of this request.
+
+
+ The status code of the command response.
+
+
+ The error message.
+
+
+
+ A response indicating the result of an entity query request.
+
+
+
+
+ The outgoing request ID for which there was a response. Matches the request ID returned
+ by a previous call to Connection.SendEntityQueryRequest.
+
+
+
+ The status code of the command response.
+
+
+ The error message.
+
+
+ The number of entities that matched the query.
+
+ Note that a best-effort attempt is made to count the entities when the status code is
+ StatusCode.ApplicationError. In this case, the count can still be non-zero, but should
+ be considered a lower bound (i.e. there might be entities matching the query that were
+ not counted).
+
+
+
+ The result of the query. Not used for CountResultType queries.
+
+ Note that a best-effort attempt is made to get results when the status code is
+ StatusCode.ApplicationError. In this case, the result can still be non-empty, but should
+ be considered incomplete (i.e. there might be entities matching the query that were not
+ returned).
+
+
+
+
+ Data for an operation that indicates a component has been added to an existing entity in the
+ worker's view of the simulation.
+
+
+
+
+ Data for an operation that indicates a component has been removed from an existing entity in
+ the worker's view of the simulation.
+
+
+
+
+ Data for an operation that indicates the worker's authority over a component for an entity
+ has been changed.
+
+
+
+
+ Data for an operation that indicates the component for an entity has been updated.
+
+
+
+
+ Data for an operation that indicates a command request has been received for a component on an
+ entity over which this worker has authority. The worker should respond to the command by
+ calling Connection.SendCommandResponse with the given request ID.
+
+
+
+
+ The incoming request ID. Should be passed to Connection.SendCommandResponse in
+ order to respond to this request.
+
+
+
+ The ID of the target entity of this request.
+
+
+
+ An upper bound on the timeout of this request. Any response sent after the timeout has
+ expired will be ignored by the SDK.
+
+
+
+ The ID of the worker that initiated this request.
+
+
+ The attribute set of the worker that initiated this request.
+
+
+ The request data.
+
+
+
+ Data for an operation that indicates a command response has been received for a request
+ previously issued by this worker. The request ID will match a previous call to
+ Connection.SendCommandRequest.
+
+
+
+
+ The outgoing request ID for which there was a response. Matches the request ID returned
+ by a previous call to Connection.SendCommandRequest.
+
+
+
+ The target entity ID of the original request.
+
+
+ The status code of the command response.
+
+
+
+ A description of the status. Will contain the reason for failure if unsuccessful.
+
+
+
+
+ The command response data. Present exactly when the status code is StatusCode.Success.
+
+
+
+
+ A Dispatcher processes OpLists retrieved from the Connection and invokes appropriate
+ callbacks.
+
+
+ This object should not be modified concurrently by multiple threads.
+
+
+
+
+
+
+
+ Registers a callback to be invoked when the Connection has disconnected and can no
+ longer be used.
+
+
+
+
+ Registers a callback to be invoked when a worker flag is changed
+
+
+
+
+ Registers a callback to be invoked when the SDK logs a message.
+
+
+
+
+ Registers a callback to be invoked when the SDK reports built-in metrics.
+
+
+
+
+ Registers a callback to be invoked when the message stream enters or leaves a critical
+ section.
+
+
+
+
+ Registers a callback to be invoked when an entity is added to the worker's view of the
+ simulation.
+
+
+
+
+ Registers a callback to be invoked when an entity is removed from the worker's view of
+ the simulation.
+
+
+
+
+ Registers a callback to be invoked when an entity ID reservation response is received.
+
+
+
+
+ Registers a callback to be invoked when a multiple entity ID reservation response is received.
+
+
+
+
+ Registers a callback to be invoked when an entity creation response is received.
+
+
+
+
+ Registers a callback to be invoked when an entity deletion response is received.
+
+
+
+
+ Registers a callback to be invoked when an entity query response is received.
+
+
+
+
+ Registers a callback to be invoked when a particular component is added to an existing
+ entity in the worker's view of the sumulation.
+
+
+
+
+ Registers a callback to be invoked when a particular component is removed from an
+ existing entity in the worker's view of the simulation.
+
+
+
+
+ Registers a callback to be invoked when the worker is granted authority over a
+ particular component for some entity, or when the worker's authority over that component
+ is revoked.
+
+
+
+
+ Registers a callback to be invoked when a particular component is updated for an entity.
+
+
+
+
+ Registers a callback to be invoked when a command request is received for a particular
+ component.
+
+
+
+
+ Registers a callback to be invoked when a command response is received for a particular
+ component.
+
+
+
+
+ Unregisters a callback identified by its CallbackKey, as returned from the registration
+ function. If the key does not exist, an exception will be thrown.
+
+
+
+
+ Processes an OpList and invokes registered callbacks.
+
+
+
+
+ Provides facilities for manipulating components in a type-safe way when the component type is
+ not known statically.
+
+
+
+
+ This interface should be implemented to provide component-specific behaviour
+ for the ForComponent
and ForEachComponent
methods.
+
+
+
+
+ Called by ForComponent
and ForEachComponent
, passing the
+ metaclass of a particular component.
+
+
+
+
+ Returns the component ID of a given component.
+
+
+
+
+ Returns the set of all known component IDs.
+
+
+
+
+ Invokes the Accept
method on the provided handler with appropriate
+ arguments for the component whose ID matches the given component ID.
+
+
+
+
+ Invokes the Accept
method on the provided handler with appropriate
+ arguments for every known component.
+
+
+
+
+ Stores the complete data for an entity's components. This is used both for representing the
+ initial set of components for an entity by the AddEntity operation, and inside the
+ (optional) View.
+
+
+ Note that an Entity object is simply a local data structure, and changes made here are not
+ automatically reflected across the SpatialOS simulation. To synchronize component state with
+ SpatialOS, use Connection.SendComponentUpdate. This object should not be modified
+ concurrently by multiple threads.
+
+
+
+
+ Retrieves data for the given component. Returns an empty option if the entity does not have the
+ given component.
+
+
+
+
+ Creates the given component with initial data. Has no effect if the entity already has
+ the given component.
+
+
+
+
+ Applies an update to the given component. Has no effect if the entity does not have the
+ given component.
+
+
+
+
+ Removes a component.
+
+
+
+
+ Returns the set of IDs of the components present in this entity.
+
+
+
+
+ A class representing the standard future concept. It can be used for both synchronous
+ and asynchronous interaction.
+
+ The type of object the future returns.
+
+
+
+ Objects of this class can be created by the SDK only.
+
+
+
+
+
+
+
+ Waits until the result becomes available, and returns it. If the result was already
+ obtained by a previous call to Get() or Get(timeoutMillis), this function returns it
+ immediately.
+
+ The result.
+
+
+
+ Waits for the result to become available. Blocks until the specified timeout has
+ elapsed or the result has become available, whichever comes first. If the result was
+ already obtained by a previous call to Get() or Get(timeoutMillis), this function
+ returns it immediately.
+
+ The time to wait for the result to become available.
+ The result if it is available; an empty option, otherwise.
+
+
+
+ Details for a specific deployment obtained via Locator.GetDeploymentList.
+
+
+
+
+ The name of the deployment.
+
+
+
+
+ The name of the assembly used by this deployment.
+
+
+
+
+ Description of this deployment.
+
+
+
+
+ Number of users currently connected to the deployment.
+
+
+
+
+ Total user capacity of the deployment.
+
+
+
+
+ A deployment list obtained via Locator.GetDeploymentList.
+
+
+
+
+ List of accessible deployments for the given project.
+
+
+
+
+ Will be non-null if an error occurred.
+
+
+
+
+ A queue status update when connecting to a deployment via Locator.Connect.
+
+
+
+
+ Position in the queue. Decreases as we advance to the front of the queue.
+
+
+
+
+ Will be non-null if an error occurred.
+
+
+
+
+ A client which can be used to connect to a SpatialOS deployment via a locator service.
+ This is the standard flow used to connect a local worker to a cloud deployment.
+
+
+ This object should not be used concurrently by multiple threads.
+
+
+
+
+ Creates a client for the locator service.
+
+ The hostname of the locator service. Typically either
+ "locator.improbable.io" (for production) or "locator-staging.improbable.io"
+ (for staging).
+ The parameters for the locator service.
+
+
+
+
+
+
+ Queries the current list of deployments for the project specified in the
+ LocatorParameters. The resulting future can be used to make this connection either
+ synchronously or asyncronously.
+
+ A future object for the list of deployments.
+
+
+
+
+ Connects to a specific deployment. The resulting future can be used to make this
+ connection either synchronously or asyncronously.
+
+ The deployment name, which should be obtained by calling
+ GetDeploymentListAsync.
+ The connection parameters.
+ The queueing callback, which should return false to cancel
+ queuing, or true to continue queueing.
+ A future object for the connection.
+
+
+
+
+ A histogram metric tracks observations of a given value by bucket. This corresponds to a
+ Prometheus histogram metric.
+
+ This object should not be used concurrently by multiple threads.
+
+
+
+
+ A histogram bucket.
+
+
+
+
+ The upper bound.
+
+
+
+
+ The number of observations that were less than or equal to the upper bound.
+
+
+
+
+ Creates a histogram with the given bucket boundaries. Each bucket boundary is an upper
+ bound; the bucket tracks all observations with a value less than or equal to the bound.
+ A final bucket with a boundary of +INF is added automatically.
+
+
+
+
+ Creates a histogram with a single bucket.
+
+
+
+
+ Clears all recorded oservations. Automatically called by Connection.SendMetrics.
+
+
+
+
+ Records a sample and adds it to the corresponding buckets.
+
+
+
+
+ A set of metrics sent up from a worker to SpatialOS.
+
+
+ Keys for the contained metrics should match the following regex:
+ [a-zA-Z_][a-zA-Z0-9_]*
+
+
+
+
+ Copies all metrics from another Metrics object into this one, overwriting existing values.
+
+
+
+
+ The load value of this worker. A value of 0 indicates that the worker is completely
+ unloaded; a value greater than 1 indicates that the worker is overloaded. The load value
+ directs SpatialOS's load-balancing strategy for managed workers (spinning them up,
+ spinning them down, and assigning work between them).
+
+
+
+
+ Gauge metrics for the worker.
+
+
+
+
+ Histogram metrics for the worker.
+
+
+
+
+ Time (in milliseconds) that RakNet should use for its heartbeat protocol.
+
+
+
+
+ Number of multiplexed TCP connections.
+
+
+
+
+ Size in bytes of the TCP send buffer.
+
+
+
+
+ Size in bytes of the TCP receive buffer.
+
+
+
+
+ Whether to enable TCP_NODELAY.
+
+
+
+
+ Whether to connect to SpatialOS using the internal IP address. This is for managed
+ workers that run in the cloud alongside SpatialOS.
+
+
+
+
+ Type of network connection to use when connecting to SpatialOS.
+
+
+
+
+ Connection parameters specific to RakNet connections.
+
+
+
+
+ Connection parameters specific to TCP connections.
+
+
+
+
+ Log file names are prefixed with this prefix, are numbered, and have the extension .log.
+
+
+
+
+ Maximum number of log files to keep. Note that logs from any previous protocol logging
+ sessions will be overwritten.
+
+
+
+
+ Once the size of a log file reaches this size, a new log file is created.
+
+
+
+
+ Worker type (platform).
+
+
+
+
+ Parameters controlling the network connection to SpatialOS.
+
+
+
+
+ Number of messages that can be stored on the send queue. When the send queue is full,
+ calls to Connection.Send functions can block.
+
+
+
+
+ Number of messages that can be stored on the receive queue. When the receive queue is
+ full, SpatialOS can apply QoS and drop messages to the worker.
+
+
+
+
+ Number of messages logged by the SDK that can be stored in the log message queue. When
+ the log message queue is full, messages logged by the SDK can be dropped.
+
+
+
+
+ The Connection tracks several internal metrics, such as send and receive queue
+ statistics. This parameter controls how frequently the Connection will return a
+ MetricsOp reporting its built-in metrics. If set to zero, this functionality is
+ disabled.
+
+
+
+
+ Parameters for configuring protocol logging.
+
+
+
+
+ Whether to enable protocol logging at startup.
+
+
+
+
+ The token would typically be provided on the command-line by the SpatialOS
+ launcher.
+
+
+
+
+ Steam ticket for the steam app ID and publisher key corresponding to the project name
+ specified in the LocatorParameters. Typically obtained from the steam APIs.
+
+
+
+
+ Deployment tag to request access for. If non-empty, must match the following regex:
+ [A-Za-z0-9][A-Za-z0-9_]*
+
+
+
+
+ The name of the SpatialOS project.
+
+
+
+
+ Type of credentials to use when authenticating via the Locator.
+
+
+
+
+ Parameters used if the CredentialsType is LoginToken.
+
+
+
+
+ Parameters used if the CredentialsType is Steam.
+
+
+
+ Base class for entity query constraints.
+
+
+ Constrains a query to match only entities with a particular ID.
+
+
+ Constrains a query to match only entities that have a specific component.
+
+
+
+ Constrains a query to match only entities whose position lies within a given sphere.
+
+
+
+
+ Constrains a query by the conjunction of one or more constraints.
+
+
+
+
+ Constrains a query by the disjunction of one or more constraints.
+
+
+
+
+ Constrains a query by negating a constraint.
+
+
+
+ Base class for entity query result types.
+
+
+ Indicates that a query should return the number of entites it matched.
+
+
+
+ Indicates that a query should return a component data snapshot for each matched entity.
+
+
+
+
+ If nonempty, filters the components returned in the snapshot for each entity.
+
+
+
+ Represents a global query for entity data across the simulation.
+
+
+ Type parameter for entity ID reservation request IDs.
+
+
+ Type parameter for multiple entity ID reservation request IDs.
+
+
+ Type parameter for entity creation request IDs.
+
+
+ Type parameter for entity deletion request IDs.
+
+
+ Type parameter for entity query request IDs.
+
+
+ Type parameter for outgoing entity command request IDs.
+
+
+ Type parameter for incoming entity command request IDs.
+
+
+
+ Represents an ID for a request. The type parameter should be one of the marker interfaces
+ defined above.
+
+
+
+
+ The underlying raw ID of the request. Only use this if you know what you
+ are doing; prefer to use the RequestId object instead.
+
+
+
+
+ A stream for reading snapshot one Entity at a time.
+
+
+
+
+ Creates a SnapshotInputStream to read the Snapshot at the given string path.
+
+
+
+
+ Releases the resources of the SnapshotInputStream.
+
+
+
+
+ Reads the next EntityId entityId, Entity entity pair from the Snapshot.
+
+ Returns an optional string error message if an error occurs during reading.
+
+
+
+
+ Returns true if the SnapshotInputStream has not reached the end of the Snapshot.
+
+
+
+
+ A stream for outputting entities to a Snapshot one at a time.
+
+
+
+
+ Creates a SnapshotOutputStream to write a Snapshot at the given string path.
+
+
+
+
+ Writes the end of Snapshot header and releases the resources of the SnapshotOutputStream.
+
+
+
+
+ Writes the EntityId entityId, Entity entity pair to the Snapshot.
+
+ Returns an Optional string error message if an error occurs during writing.
+
+
+
+
+ The View is an optional data structure that maintains the known state of entities in the worker's
+ view of the simulation.
+
+
+ This object should not be modified concurrently by multiple threads. Note that as of
+ SpatialOS 11.1 this class is intended primarily as an example: using the
+ Improbable.Worker.Dynamic
functionality, a custom View
can be
+ implemented from scratch with any semantics desired.
+
+
+
+
+ Current component data for all entities in the worker's view.
+
+
+
+
+ Current authority delegations.
+
+
+
+
+ Helper function that checks if the worker has authority over a particular component of a
+ particular entity.
+
+
+
+
+ Merge toAdd and toRemove with map.
+
+
+
+
+ Registers that a call is entered to update the state of the map accordingly.
+
+
+
+
+ Registers that a call is exited to update the state of the map accordingly.
+
+
+
+
+ Checks that a disposableObject representing an unmanaged handle is not disposed
+ (assuming that in such a context it can no longer be used). If the handle has been
+ disposed of, this throws an ObjectDisposedException. Otherwise, nothing happens.
+
+
+
+
+ This attribute is valid on static functions and it is used by Mono's
+ Ahead of Time Compiler (AOT) to generate the code necessary to support
+ native calls back into managed code.
+
+
+ Implemented here as a custom attribute as we do not include Xamarin's
+ Mono library within the C# Worker SDK layer itself. Based on the official Mono
+ implementation.
+ See: https://github.com/mono/mono/blob/master/mcs/class/System/Mono.Util/MonoPInvokeCallbackAttribute.cs
+ See: https://developer.xamarin.com/api/type/MonoTouch.MonoPInvokeCallbackAttribute/
+
+
+
+
+ Manages the associations between unique component IDs and instances of metaclasses.
+
+
+
+
+ Maps component IDs to their metaclass.
+
+
+
+
+ Looks up a metaclass instance from a component ID.
+ Returns null if the componentId is unknown.
+
+
+
+
+ Returns the component ID associated with a metaclass.
+
+
+ is not a known metaclass.
+
+
+
+
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.xml.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.xml.meta
new file mode 100644
index 0000000..95061bf
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/Improbable.WorkerSdkCsharp.xml.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 246630db4b1384339bc2ea8c1003b825
+timeCreated: 1525947639
+licenseType: Pro
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/System.Threading.dll b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/System.Threading.dll
new file mode 100644
index 0000000..0230d71
Binary files /dev/null and b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/System.Threading.dll differ
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/System.Threading.dll.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/System.Threading.dll.meta
new file mode 100644
index 0000000..9c2e864
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Dll/System.Threading.dll.meta
@@ -0,0 +1,32 @@
+fileFormatVersion: 2
+guid: e46d97f5358a3cf438cb2c18a45d0c5d
+timeCreated: 1521737937
+licenseType: Pro
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ isPreloaded: 0
+ isOverridable: 0
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ - first:
+ Windows Store Apps: WindowsStoreApps
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src.meta
new file mode 100644
index 0000000..313f5ca
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 6211ca704866c7144885e9bc19eec81b
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor.meta
new file mode 100644
index 0000000..b1eeb52
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 493074f38a060974db09660c701e59fc
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/.gitignore b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/.gitignore
new file mode 100644
index 0000000..24d6d77
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/.gitignore
@@ -0,0 +1,3 @@
+# Needed because we generally ignore folders named build. This makes git not ignore the directory.
+!Build
+
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Addons.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Addons.meta
new file mode 100644
index 0000000..8f1742a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Addons.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 89c4660fa37cec541af037a0dfce3042
+folderAsset: yes
+timeCreated: 1484301362
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Addons/SpatialCommand.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Addons/SpatialCommand.cs
new file mode 100644
index 0000000..b29cd45
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Addons/SpatialCommand.cs
@@ -0,0 +1,290 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using Improbable.Unity.Editor.Core;
+using Improbable.Unity.Util;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.Editor.Addons
+{
+ ///
+ /// Allow each user to specify the location of the spatial command.
+ ///
+ [InitializeOnLoad]
+ public sealed class SpatialCommand : ISpatialOsEditorAddon, ISpatialOsEditorAddonSettings
+ {
+ private static readonly SpatialCommand instance;
+
+ internal const string usrLocalBin = "/usr/local/bin";
+
+ private string spatialLocation;
+ private string oldSpatialLocation;
+ private GUIContent buttonContent;
+ private GUIStyle iconStyle;
+ private string discoveredLocation;
+ internal Func> GetCommandLine { get; set; }
+ internal Func GetUserString { get; set; }
+ internal Func FileExists { get; set; }
+ internal Func GetEnvironmentVariable { get; set; }
+
+ public const string SpatialPathArgument = "spatialCommandPath";
+
+
+ ///
+ public string Name
+ {
+ get { return "Spatial CLI [Built-in]"; }
+ }
+
+ ///
+ public string Vendor
+ {
+ get { return "Improbable Worlds, Ltd."; }
+ }
+
+ ///
+ /// Returns the user-configured location of spatial[.exe], or simply "spatial" if it's not set.
+ ///
+ ///
+ /// If +spatialCommandPath "/path/to/spatial" is specified on the command line, it is used in preference to any user
+ /// settings.
+ /// By default, it is assumed that "spatial" will be on the system PATH.
+ ///
+ public static string SpatialPath
+ {
+ get { return instance.GetSpatialPath(); }
+ }
+
+ ///
+ /// Starts a process and ensures that fullPathToSpatial is available in the PATH environment variable.
+ ///
+ public static Process RunCommandWithSpatialInThePath(string fullPathToSpatial, ProcessStartInfo startInfo)
+ {
+ return SpatialRunner.RunCommandWithSpatialInThePath(fullPathToSpatial, startInfo);
+ }
+
+ ///
+ public void OnSettingsGui(Rect rect)
+ {
+ InitializeOnce();
+
+ using (new EditorGUILayout.VerticalScope())
+ {
+ DrawSpatialLocationInput();
+
+ DrawDiscoveredLocation();
+
+ DrawUpdateButton();
+ }
+ }
+
+ private void DrawUpdateButton()
+ {
+ using (new EditorGUILayout.VerticalScope())
+ {
+ EditorGUILayout.LabelField("Update Spatial CLI Version");
+ using (new EditorGUI.DisabledScope(string.IsNullOrEmpty(discoveredLocation)))
+ {
+ if (GUILayout.Button(buttonContent))
+ {
+ SpatialOsEditor.RunPausedProcess(SpatialPath, "update", "");
+ }
+ }
+ }
+ }
+
+ private void InitializeOnce()
+ {
+ if (spatialLocation == null)
+ {
+ spatialLocation = GetUserString(SpatialRunner.CommandLocationKey, string.Empty);
+ discoveredLocation = DiscoverSpatialLocationForDisplay(spatialLocation);
+ }
+
+ if (buttonContent == null)
+ {
+ buttonContent = new GUIContent("Update") { tooltip = "Update spatial to the latest version" };
+ }
+
+ if (iconStyle == null)
+ {
+ iconStyle = new GUIStyle() { fixedWidth = 24, fixedHeight = 24 };
+ }
+ }
+
+ private void DrawSpatialLocationInput()
+ {
+ using (var check = new EditorGUI.ChangeCheckScope())
+ using (new EditorGUILayout.VerticalScope())
+ {
+ EditorGUILayout.PrefixLabel("Spatial CLI location");
+
+ oldSpatialLocation = spatialLocation;
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ EditorGUILayout.HelpBox(spatialLocation, MessageType.None);
+ if (GUILayout.Button("...", GUILayout.ExpandWidth(false)))
+ {
+ BrowseForSpatial();
+ }
+
+ if (GUILayout.Button("Reset to default", GUILayout.ExpandWidth(false)))
+ {
+ spatialLocation = string.Empty;
+ }
+ }
+
+ if (check.changed && oldSpatialLocation != spatialLocation)
+ {
+ CommitAndCheckSpatialLocation();
+ }
+ }
+ }
+
+ private void BrowseForSpatial()
+ {
+ var extension = string.Empty;
+ if (Application.platform == RuntimePlatform.WindowsEditor)
+ {
+ extension = "exe";
+ }
+
+ spatialLocation = EditorUtility.OpenFilePanel("Find spatial", spatialLocation, extension);
+ }
+
+ private void CommitAndCheckSpatialLocation()
+ {
+ if (string.IsNullOrEmpty(spatialLocation))
+ {
+ EditorPrefs.DeleteKey(SpatialRunner.CommandLocationKey);
+ }
+ else
+ {
+ EditorPrefs.SetString(SpatialRunner.CommandLocationKey, spatialLocation);
+ }
+
+ discoveredLocation = DiscoverSpatialLocationForDisplay(spatialLocation);
+ }
+
+ private void DrawDiscoveredLocation()
+ {
+ if (string.IsNullOrEmpty(discoveredLocation))
+ {
+ EditorGUILayout.HelpBox("Could not find spatial.", MessageType.Error);
+ }
+ else
+ {
+ EditorGUILayout.HelpBox(string.Format("Found {0}.", discoveredLocation), MessageType.Info);
+ }
+ }
+
+ private string DiscoverSpatialLocationForDisplay(string location)
+ {
+ bool viaPath;
+ var path = DiscoverSpatialLocation(location, out viaPath);
+ if (viaPath)
+ {
+ return path + " (via PATH)";
+ }
+ else
+ {
+ return path;
+ }
+ }
+
+ private string DiscoverSpatialLocation(string location)
+ {
+ bool viaPath;
+ return DiscoverSpatialLocation(location, out viaPath);
+ }
+
+ private string DiscoverSpatialLocation(string location, out bool viaPath)
+ {
+ viaPath = false;
+ if (string.IsNullOrEmpty(location))
+ {
+ var pathValue = GetEnvironmentVariable("PATH");
+ if (pathValue == null)
+ {
+ return string.Empty;
+ }
+
+ var fileName = SpatialRunner.DefaultSpatialCommand;
+ if (Application.platform == RuntimePlatform.WindowsEditor)
+ {
+ fileName = Path.ChangeExtension(fileName, ".exe");
+ }
+
+ var splitPath = pathValue.Split(Path.PathSeparator);
+
+ if (Application.platform == RuntimePlatform.OSXEditor && !splitPath.Contains(usrLocalBin))
+ {
+ splitPath = splitPath.Union(new[] { usrLocalBin }).ToArray();
+ }
+
+ foreach (var path in splitPath)
+ {
+ var testPath = Path.Combine(path, fileName);
+ if (FileExists(testPath))
+ {
+ viaPath = true;
+ return testPath;
+ }
+ }
+ }
+ else
+ {
+ var fullLocation = location;
+ if (Application.platform == RuntimePlatform.WindowsEditor)
+ {
+ fullLocation = Path.ChangeExtension(fullLocation, ".exe");
+ }
+
+ if (FileExists(fullLocation))
+ {
+ return fullLocation;
+ }
+ }
+
+ return string.Empty;
+ }
+
+ static SpatialCommand()
+ {
+ instance = new SpatialCommand();
+ SpatialOsEditor.RegisterAddon(instance);
+ }
+
+ internal SpatialCommand()
+ {
+ GetCommandLine = Environment.GetCommandLineArgs;
+ GetUserString = EditorPrefs.GetString;
+ FileExists = File.Exists;
+ GetEnvironmentVariable = Environment.GetEnvironmentVariable;
+ }
+
+ internal string GetSpatialPath()
+ {
+ string path;
+ // The command line overrides everything.
+ if (!CommandLineUtil.TryGetCommandLineValue(GetCommandLine(), SpatialPathArgument, out path))
+ {
+ // Then try the user-specific preferences
+ path = GetUserString(SpatialRunner.CommandLocationKey, string.Empty);
+ }
+
+ // If nothing has been configured, assume it's on the system PATH, and use a sensible default of "spatial"
+ if (string.IsNullOrEmpty(path))
+ {
+ path = DiscoverSpatialLocation(null);
+ }
+
+ return path;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Addons/SpatialCommand.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Addons/SpatialCommand.cs.meta
new file mode 100644
index 0000000..fff6154
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Addons/SpatialCommand.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 8dd53f1d2784560428565c1ee01a2596
+timeCreated: 1484301362
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Assets.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Assets.meta
new file mode 100644
index 0000000..54ce7f5
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Assets.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 8be51be25a9e5c747a18ccdf1945b93a
+folderAsset: yes
+timeCreated: 1444833351
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Assets/EditorPrefabGameObjectLoader.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Assets/EditorPrefabGameObjectLoader.cs
new file mode 100644
index 0000000..7268f40
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Assets/EditorPrefabGameObjectLoader.cs
@@ -0,0 +1,93 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Improbable.Assets;
+using Improbable.Unity.EditorTools.PrefabExport;
+using Improbable.Unity.EditorTools.Util;
+using Improbable.Unity.Util;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Assets
+{
+ public class EditorPrefabGameObjectLoader : IAssetLoader
+ {
+ private readonly IDictionary prefabs = new Dictionary();
+
+ public EditorPrefabGameObjectLoader()
+ {
+ CleanOutputFolder();
+ FindPrefabsInProject();
+ }
+
+ public void LoadAsset(string prefabName, Action onGameObjectLoaded, Action onError)
+ {
+ try
+ {
+ string path;
+ if (prefabs.TryGetValue(prefabName, out path))
+ {
+ var source = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject;
+ var prefabPath = Path.Combine(EditorPaths.PrefabCompileDirectory, prefabName + ".prefab");
+ var prefabGameObject = PrefabUtility.CreatePrefab(prefabPath.ToUnityPath(), source);
+ if (prefabGameObject == null)
+ {
+ onError(new Exception(string.Format("Could not load the game object from the local prefab '{0}'.", prefabName)));
+ }
+ else
+ {
+ onGameObjectLoaded(prefabGameObject);
+ }
+ }
+ else
+ {
+ onError(new Exception(string.Format("Could not find the local prefab '{0}'.", prefabName)));
+ }
+ }
+ catch (Exception ex)
+ {
+ onError(ex);
+ }
+ }
+
+ public void CancelAllLoads()
+ {
+ // LoadAsset is instantaneous, so no need to do anything here.
+ }
+
+ private void FindPrefabsInProject()
+ {
+ var guids = EntityPrefabExporter.GetAllPrefabAssetGuids();
+ foreach (var guid in guids)
+ {
+ var path = AssetDatabase.GUIDToAssetPath(guid);
+ var name = Path.GetFileNameWithoutExtension(path);
+ if (string.IsNullOrEmpty(name))
+ {
+ Debug.LogWarningFormat("Found a prefab an empty name on path '{0}'. Please give it a name.", path);
+ }
+ else if (prefabs.ContainsKey(name))
+ {
+ Debug.LogWarningFormat("Duplicate Prefab detected: {0}", path);
+ }
+ else
+ {
+ prefabs.Add(name, path);
+ }
+ }
+ }
+
+ private static void CleanOutputFolder()
+ {
+ Directory.CreateDirectory(EditorPaths.PrefabCompileDirectory);
+ var info = new DirectoryInfo(EditorPaths.PrefabCompileDirectory);
+ var files = info.GetFiles();
+ foreach (var fileInfo in files)
+ {
+ fileInfo.Delete();
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Assets/EditorPrefabGameObjectLoader.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Assets/EditorPrefabGameObjectLoader.cs.meta
new file mode 100644
index 0000000..523eb63
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Assets/EditorPrefabGameObjectLoader.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: c0b626a98ab026e4d845fc8144d1286d
+timeCreated: 1444833361
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build.meta
new file mode 100644
index 0000000..a52f3ac
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: f0a3c1d2eaf553748a3ca862aee0a688
+folderAsset: yes
+timeCreated: 1444833352
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/DefaultPlayerBuildConfiguration.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/DefaultPlayerBuildConfiguration.cs
new file mode 100644
index 0000000..06ad55a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/DefaultPlayerBuildConfiguration.cs
@@ -0,0 +1,62 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.Collections.Generic;
+using System.IO;
+using Improbable.Unity.Assets;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ static class DefaultPlayerBuildConfiguration
+ {
+ private static readonly List CurrentPlatformTargetList = new List { "Current" };
+
+ internal static PlayerBuildConfiguation Generate()
+ {
+ var config = new PlayerBuildConfiguation
+ {
+ Deploy = new Enviroment
+ {
+ UnityWorker = new Config
+ {
+ Assets = AssetDatabaseStrategy.Streaming.ToString(),
+ Targets = new List
+ {
+ BuildTarget.StandaloneLinux64 + "?" + BuildOptions.EnableHeadlessMode
+ }
+ },
+ UnityClient = new Config
+ {
+ Assets = AssetDatabaseStrategy.Streaming.ToString(),
+ Targets = new List
+ {
+ BuildTarget.StandaloneWindows.ToString(),
+#if UNITY_2017_3_OR_NEWER
+ BuildTarget.StandaloneOSX.ToString()
+#else
+ BuildTarget.StandaloneOSXIntel64.ToString()
+#endif
+ }
+ }
+ },
+ Develop = new Enviroment
+ {
+ UnityWorker = new Config
+ {
+ Assets = AssetDatabaseStrategy.Streaming.ToString(),
+ Targets = CurrentPlatformTargetList
+ },
+ UnityClient = new Config
+ {
+ Assets = AssetDatabaseStrategy.Streaming.ToString(),
+ Targets = CurrentPlatformTargetList
+ },
+ }
+ };
+ var json = JsonUtility.ToJson(config, prettyPrint: true);
+ File.WriteAllText(UnityPlayerBuilders.PlayerConfigurationFilePath, json);
+ return config;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/DefaultPlayerBuildConfiguration.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/DefaultPlayerBuildConfiguration.cs.meta
new file mode 100644
index 0000000..dc4f32f
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/DefaultPlayerBuildConfiguration.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 2fdf9a492d17e47bc888d1dae273b1ec
+timeCreated: 1452782483
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPackager.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPackager.cs
new file mode 100644
index 0000000..5bde22c
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPackager.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ [Obsolete("Obsolete in 10.3.0. Please see IPlayerBuildEvents for information about customizing player packaging.")]
+ public interface IPackager
+ {
+ /// the working directory that unity has built into.
+ ///
+ /// An IPackager takes a built Unity player and calls Prepare with the path where it's located.
+ /// It gives you the opportunity to copy over any extra files that the worker needs to be run,
+ /// before it is packaged, ready for consumption by SpatialOS.
+ /// To configure the IPackager in use, set the UnityPlayerBuilder.GetPackager function to return your custom packager.
+ ///
+ void Prepare(string packagePath);
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPackager.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPackager.cs.meta
new file mode 100644
index 0000000..0c0fc86
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPackager.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 7550b0a77ccda5a408aa9ec917291a1e
+timeCreated: 1444833359
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPlayerBuildEvents.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPlayerBuildEvents.cs
new file mode 100644
index 0000000..598f41d
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPlayerBuildEvents.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using UnityEditor;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ ///
+ /// Inherit from this interface to provide custom behavior related to the SpatialOS player building process.
+ ///
+ public interface IPlayerBuildEvents
+ {
+ ///
+ /// Called before any players are built.
+ ///
+ void BeginBuild();
+
+ ///
+ /// Called after all players are built.
+ ///
+ ///
+ /// This will always be called, even if all players are not successfully built.
+ ///
+ void EndBuild();
+
+ ///
+ /// Called between and , for each worker type that is built.
+ /// Please reference for more information about how the returned array of
+ /// scenes is used by Unity's build process.
+ ///
+ /// The type of the worker being built.
+ /// An array of all scenes that should be included in the build.
+ string[] GetScenes(WorkerPlatform workerType);
+
+ ///
+ /// Called between and , for each worker type that is packaged.
+ /// Implement this to modify the contents of the directory before packaging.
+ ///
+ /// The type of the worker being packaged.
+ /// The target platform for the player being packaged.
+ /// The configuration associated with the player being packaged.
+ /// The path that will be packaged.
+ void BeginPackage(WorkerPlatform workerType, BuildTarget target, Config config, string packagePath);
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPlayerBuildEvents.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPlayerBuildEvents.cs.meta
new file mode 100644
index 0000000..a327fc0
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IPlayerBuildEvents.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: c28a21ba747b50b4b8cd5b887bbbc788
+timeCreated: 1491492707
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IWorkerProvider.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IWorkerProvider.cs
new file mode 100644
index 0000000..c73f9be
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IWorkerProvider.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.Collections.Generic;
+using Improbable.Unity.Editor.Core;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ interface IWorkerProvider
+ {
+ IList GetWorkers();
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IWorkerProvider.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IWorkerProvider.cs.meta
new file mode 100644
index 0000000..ee9f9ba
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/IWorkerProvider.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: cbab9ebe0a02a4644a32c7ca474cafe5
+timeCreated: 1494259756
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerBuildConfiguation.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerBuildConfiguation.cs
new file mode 100644
index 0000000..8f37a44
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerBuildConfiguation.cs
@@ -0,0 +1,90 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ ///
+ /// The class that represents the structure of the whole player-build-config.json file, i.e.
+ /// the configuration for building Unity players.
+ ///
+ [Serializable]
+ public class PlayerBuildConfiguation
+ {
+ // This field is always initialised with a new instance of
+ // GlobalConfig. By convention, we do not serialise it to
+ // the JSON output.
+ [NonSerialized] [Obsolete("Field Global is deprecated and will be removed in an upcoming SpatialOS version.")]
+ public GlobalConfig Global = new GlobalConfig();
+
+ public Enviroment Deploy;
+ public Enviroment Develop;
+ }
+
+ ///
+ /// The global build configuration that applies to all built players.
+ ///
+ [Serializable]
+ [Obsolete("GlobalConfig is deprecated and will be removed in an upcoming SpatialOS version.")]
+ public class GlobalConfig
+ {
+ public PluginConfig Plugin = new PluginConfig();
+ }
+
+ ///
+ /// The global build configuration for Unity plugins.
+ ///
+ [Serializable]
+ [Obsolete("PluginConfig is deprecated and will be removed in an upcoming SpatialOS version.")]
+ public class PluginConfig
+ {
+ public bool UsePlatformDirectories = true;
+ }
+
+ ///
+ /// The configuration for a particular build environment when building Unity players.
+ ///
+ [Serializable]
+ public class Enviroment
+ {
+ public Config UnityWorker;
+ public Config UnityClient;
+ }
+
+ ///
+ /// The build configuration for a particular Unity player.
+ ///
+ [Serializable]
+ public class Config
+ {
+ private static readonly List NoBuildOptions = new List { BuildOptions.None };
+ public List Targets = new Collections.List();
+
+ public string Assets;
+
+ public IEnumerable FlagsForPlatform(string buildTarget)
+ {
+ return default(string) == buildTarget ? NoBuildOptions : ParseFlags(buildTarget);
+ }
+
+ internal IEnumerable ParseFlags(string target)
+ {
+ var index = target.IndexOf('?');
+ if (index == -1)
+ {
+ return NoBuildOptions;
+ }
+
+ var flags = target.Substring(index + 1).Split(',').Select(s => s.Trim()).Select(ToBuildOptions);
+ return flags;
+ }
+
+ private static BuildOptions ToBuildOptions(string value)
+ {
+ return (BuildOptions) Enum.Parse(typeof(BuildOptions), value);
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerBuildConfiguation.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerBuildConfiguation.cs.meta
new file mode 100644
index 0000000..a000807
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerBuildConfiguation.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 22d3acb0441e272488419b585114e63f
+timeCreated: 1444833356
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerCompression.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerCompression.cs
new file mode 100644
index 0000000..3f8fc24
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerCompression.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ ///
+ /// Indicate whether or not built-out players should be compressed.
+ ///
+ public enum PlayerCompression
+ {
+ Enabled,
+ Disabled
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerCompression.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerCompression.cs.meta
new file mode 100644
index 0000000..b5900ca
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/PlayerCompression.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 7959fe6084e861a40ba7e1a06287096f
+timeCreated: 1470317073
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/ReloadAssemblies.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/ReloadAssemblies.cs
new file mode 100644
index 0000000..a114103
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/ReloadAssemblies.cs
@@ -0,0 +1,106 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Improbable.Unity.Editor.Addons;
+using Improbable.Unity.EditorTools.Util;
+using UnityEditor;
+using UnityEditor.Callbacks;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ internal class ReloadAssemblies
+ {
+ private class CompareBuilders : IEqualityComparer
+ {
+ public bool Equals(UnityPlayerBuilder x, UnityPlayerBuilder y)
+ {
+ if (ReferenceEquals(x, y))
+ {
+ return true;
+ }
+
+ return x.AssemblyDirectory == y.AssemblyDirectory;
+ }
+
+ public int GetHashCode(UnityPlayerBuilder obj)
+ {
+ return obj.AssemblyDirectory.GetHashCode();
+ }
+ }
+
+ [DidReloadScripts]
+ internal static void OnScriptsReloaded()
+ {
+ if (!UnityPlayerBuilderMenu.IsAutopatchEnabled())
+ {
+ return;
+ }
+
+ if (!Directory.Exists(EditorPaths.AssetDatabaseDirectory))
+ {
+ return;
+ }
+
+ if (EditorApplication.isPlayingOrWillChangePlaymode)
+ {
+ return;
+ }
+
+ // Skip repackaging during command line builds, since users are explicitly building a set of players anyway.
+ if (Environment.GetCommandLineArgs().Select(s => s.ToLowerInvariant()).Contains("-batchmode"))
+ {
+ Debug.Log("Skipping auto-patching in batchmode");
+ return;
+ }
+
+ try
+ {
+ EditorApplication.LockReloadAssemblies();
+
+ var generatedCodeSourcePaths = Directory.GetFiles(EditorPaths.AssetDirectory, "*Generated.Code.dll", SearchOption.AllDirectories);
+ var scriptPaths = Directory.GetFiles(EditorPaths.ScriptAssembliesDirectory, "*Assembly-*.dll", SearchOption.AllDirectories).Where(p => !p.Contains("-Editor"));
+
+ var allPaths = generatedCodeSourcePaths.Union(scriptPaths).Select(Path.GetFullPath).ToList();
+
+ var developmentPlayerBuilders =
+ UnityPlayerBuilders.DevelopmentPlayerBuilders(SimpleBuildSystem.GetWorkerTypesToBuild());
+
+ var deploymentPlayerBuilders =
+ UnityPlayerBuilders.DeploymentPlayerBuilders(SimpleBuildSystem.GetWorkerTypesToBuild());
+
+ var allPlayerBuilders = developmentPlayerBuilders
+ .Union(deploymentPlayerBuilders)
+ .Distinct(new CompareBuilders()).ToList();
+
+ var playerBuildEvents = SimpleBuildSystem.CreatePlayerBuildEventsAction();
+
+ foreach (var builder in allPlayerBuilders)
+ {
+ // No point in patching + packaging players that have not been built yet.
+ if (builder.AssemblyDirectoryEmpty())
+ {
+ continue;
+ }
+
+ Debug.LogFormat("Auto-patching {0} files in {1} {2}", allPaths.Count, builder.BuildTarget, builder.WorkerType);
+
+ foreach (var sourcePath in allPaths)
+ {
+ builder.PatchAssembly(sourcePath);
+ }
+
+ builder.PackagePlayer(playerBuildEvents, SpatialCommand.SpatialPath, PlayerCompression.Disabled);
+ }
+ }
+ finally
+ {
+ EditorApplication.UnlockReloadAssemblies();
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/ReloadAssemblies.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/ReloadAssemblies.cs.meta
new file mode 100644
index 0000000..b016af9
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/ReloadAssemblies.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 537a8b65886f5014e8d7554c34b17e79
+timeCreated: 1480680147
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimpleBuildSystem.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimpleBuildSystem.cs
new file mode 100644
index 0000000..7afbc9b
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimpleBuildSystem.cs
@@ -0,0 +1,275 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Improbable.Editor.Configuration;
+using Improbable.Unity.EditorTools.PrefabExport;
+using Improbable.Unity.EditorTools.Util;
+using Improbable.Unity.Util;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ ///
+ /// This is a simple default build system that will compile assets and build workers.
+ ///
+ public static class SimpleBuildSystem
+ {
+ private static string target;
+
+ ///
+ /// A list of all default types of players to build.
+ ///
+ public static readonly List AllWorkerTypes = new Collections.List
+ {
+ WorkerTypeUtils.UnityClientType,
+ WorkerTypeUtils.UnityWorkerType
+ };
+
+ ///
+ /// Override this to customize the actions that occur during "spatial worker build".
+ ///
+ public static Action BuildAction = DefaultBuild;
+
+ ///
+ /// Override this to customize the actions that occur during "spatial worker clean".
+ ///
+ public static Action CleanAction = DefaultClean;
+
+ ///
+ /// Override this to customize the actions that occur during "spatial worker codegen".
+ ///
+ public static Action CodegenAction = DefaultCodegen;
+
+ ///
+ /// Override this to provide custom build events when building players.
+ /// This will be called once each time and
+ /// is called.
+ ///
+ public static Func CreatePlayerBuildEventsAction = DefaultCreatePlayerBuildEvents;
+
+ ///
+ /// The name of the build target to run. If empty (the default) all targets will run.
+ /// Can be overridden with the IMPROBABLE_BUILD_TARGET environment variable.
+ ///
+ public static string Target
+ {
+ get
+ {
+ var envVar = Environment.GetEnvironmentVariable("IMPROBABLE_BUILD_TARGET");
+ return string.IsNullOrEmpty(envVar) ? target : envVar;
+ }
+ set { target = value; }
+ }
+
+ ///
+ /// A list of Worker names to build. If it is null or empty, then all available workers will be used.
+ ///
+ public static IList WorkersToBuild { get; set; }
+
+ ///
+ /// The name(s) of the workers to build. Specify multiple targets by separating them with a comma.
+ /// For example: "UnityClient,UnityWorker".
+ ///
+ ///
+ /// Currently, the only possible values are "UnityWorker" and "UnityClient".
+ /// Defaults to AllWorkerTypes if the flag is not specified.
+ /// If commandLine is null, defaults to using Environment.GetCommandLineArgs();
.
+ ///
+ public static IList GetWorkerTypesToBuild(string[] commandLine = null)
+ {
+ if (WorkersToBuild != null)
+ {
+ if (!WorkersToBuild.Any())
+ {
+ return AllWorkerTypes;
+ }
+
+ return WorkersToBuild;
+ }
+
+
+ if (commandLine == null)
+ {
+ commandLine = Environment.GetCommandLineArgs();
+ }
+
+ var commandLineValue = CommandLineUtil.GetCommandLineValue(commandLine, ConfigNames.BuildWorkerTypes, string.Empty);
+ if (string.IsNullOrEmpty(commandLineValue))
+ {
+ return AllWorkerTypes;
+ }
+
+ return ParseWorkerTypes(commandLineValue);
+ }
+
+ ///
+ /// This is meant to be invoked by external build processes.
+ ///
+ public static void Build()
+ {
+ BuildAction();
+ }
+
+ ///
+ /// This is meant to be invoked by external build processes.
+ ///
+ public static void Clean()
+ {
+ CleanAction();
+ }
+
+ ///
+ /// Cleans player build directories and assemblies.
+ ///
+ public static void CleanPlayers()
+ {
+ Debug.LogFormat("Starting SpatialOS Unity Clean");
+
+ if (!string.IsNullOrEmpty(Target))
+ {
+ Debug.LogFormat("Cleaning target '{0}', available targets are '{1}', '{2}'", Target, UnityPlayerBuilders.DeploymentTarget, UnityPlayerBuilders.DevelopmentTarget);
+ }
+
+ var workersToClean = GetWorkerTypesToBuild();
+
+ foreach (var playerTarget in workersToClean)
+ {
+ Debug.LogFormat("Cleaning player {0}", playerTarget);
+ }
+
+ RunIf(UnityPlayerBuilders.DevelopmentTarget, () => CleanPlayerAssemblies(UnityPlayerBuilders.DevelopmentPlayerBuilders(workersToClean)));
+ RunIf(UnityPlayerBuilders.DeploymentTarget, () => CleanPlayerAssemblies(UnityPlayerBuilders.DeploymentPlayerBuilders(workersToClean)));
+
+ Debug.LogFormat("Finished SpatialOS Unity Clean");
+ }
+
+ ///
+ /// Cleans assemblies generated by the given player builders.
+ ///
+ private static void CleanPlayerAssemblies(IList playerBuilders)
+ {
+ foreach (var builder in playerBuilders)
+ {
+ builder.Clean();
+ }
+ }
+
+ ///
+ /// Performs default build steps for a Unity worker.
+ ///
+ private static void DefaultBuild()
+ {
+ Debug.LogFormat("Starting SpatialOS Build");
+
+ if (!string.IsNullOrEmpty(Target))
+ {
+ Debug.LogFormat(@"Building target ""{0}"", available targets are ""{1}"", ""{2}""", Target, UnityPlayerBuilders.DeploymentTarget, UnityPlayerBuilders.DevelopmentTarget);
+ }
+
+ EntityPrefabExportMenus.ExportAllEntityPrefabs();
+
+ var workersToBuild = GetWorkerTypesToBuild();
+
+ foreach (var playerTarget in workersToBuild)
+ {
+ Debug.LogFormat(@"Building player {0}", playerTarget);
+ }
+
+ RunIf(UnityPlayerBuilders.DevelopmentTarget, () => UnityPlayerBuilders.BuildDevelopmentPlayers(workersToBuild));
+ RunIf(UnityPlayerBuilders.DeploymentTarget, () => UnityPlayerBuilders.BuildDeploymentPlayers(workersToBuild));
+
+ Debug.LogFormat("Finished SpatialOS Build");
+ }
+
+ ///
+ /// Performs default clean steps for a Unity worker.
+ ///
+ private static void DefaultClean()
+ {
+ EntityPrefabExportMenus.CleanAllEntityPrefabs();
+
+ CleanPlayers();
+
+ CleanDeployedFramework();
+ }
+
+ ///
+ /// Performs default codegen steps for a Unity workers.
+ ///
+ private static void DefaultCodegen()
+ {
+ Debug.Log("Default codegen");
+ }
+
+ private static IPlayerBuildEvents DefaultCreatePlayerBuildEvents()
+ {
+ return new SingleScenePlayerBuildEvents();
+ }
+
+ ///
+ /// Removes all the elements of the framework that were deployed via spatial build.
+ ///
+ private static void CleanDeployedFramework()
+ {
+ var pluginDirectories = Directory.GetDirectories(EditorPaths.PluginDirectory, EditorPaths.OrganizationName, SearchOption.AllDirectories);
+ var directoriesToClean = new[] { EditorPaths.AssetDirectory }.Union(pluginDirectories);
+
+ foreach (var directory in directoriesToClean)
+ {
+ try
+ {
+ UnityPathUtil.EnsureDirectoryRemoved(directory);
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ }
+ }
+ }
+
+ private static void RunIf(string targetName, Action action)
+ {
+ if (string.IsNullOrEmpty(Target) || Target.ToLowerInvariant() == targetName)
+ {
+ Debug.LogFormat(@"Building target ""{0}"" ", targetName);
+ try
+ {
+ action();
+ }
+ catch (Exception ex)
+ {
+ Debug.LogException(ex);
+ throw;
+ }
+ }
+ else
+ {
+ Debug.LogFormat("Skipping target '{0}', as it does not match target '{1}'", targetName, Target);
+ }
+ }
+
+ private static IList ParseWorkerTypes(string targets)
+ {
+ var playerTargets = new HashSet();
+ if (!string.IsNullOrEmpty(targets))
+ {
+ var split = targets.Trim('\"').Split(',');
+ foreach (var str in split)
+ {
+ var trimmed = str.Trim();
+ if (trimmed != WorkerTypeUtils.UnityClientType && trimmed != WorkerTypeUtils.UnityWorkerType)
+ {
+ throw new InvalidOperationException(string.Format("'{0}' is an unknown worker type. Accepted values are '{1}' and '{2}'", trimmed, WorkerTypeUtils.UnityClientType, WorkerTypeUtils.UnityWorkerType));
+ }
+
+ playerTargets.Add(trimmed);
+ }
+ }
+
+ return playerTargets.ToList();
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimpleBuildSystem.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimpleBuildSystem.cs.meta
new file mode 100644
index 0000000..f06fa51
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimpleBuildSystem.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 0b5e0dd06b65f154b87d53d25cb4e11f
+timeCreated: 1469721780
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimplePackager.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimplePackager.cs
new file mode 100644
index 0000000..8980898
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimplePackager.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ [Obsolete("Obsolete in 10.3.0. Please see IPlayerBuildEvents for information about customizing player packaging.")]
+#pragma warning disable 0618
+ public class SimplePackager : IPackager
+#pragma warning restore 0618
+ {
+ public void Prepare(string packagePath) { }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimplePackager.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimplePackager.cs.meta
new file mode 100644
index 0000000..41efe4e
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SimplePackager.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 64d0dcc10cc45ae45b958aa2b9a8d318
+timeCreated: 1444833358
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SingleScenePlayerBuildEvents.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SingleScenePlayerBuildEvents.cs
new file mode 100644
index 0000000..73731a4
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SingleScenePlayerBuildEvents.cs
@@ -0,0 +1,81 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.Collections.Generic;
+using System.IO;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ ///
+ /// Opens a single Unity scene per worker type.
+ ///
+ public class SingleScenePlayerBuildEvents : IPlayerBuildEvents
+ {
+ // During the build we will change the scene - remember so we can change it back.
+ private string savedScenePath;
+
+ private Dictionary workerToScene = new Dictionary();
+
+ public SingleScenePlayerBuildEvents()
+ {
+ workerToScene[WorkerPlatform.UnityClient] = Path.Combine("Assets", "ClientScene.unity");
+ workerToScene[WorkerPlatform.UnityWorker] = Path.Combine("Assets", "PhysicsServerScene.unity");
+ }
+
+ ///
+ /// Provides a mapping from the worker type to a path to a scene to load.
+ ///
+ public Dictionary WorkerToScene
+ {
+ get { return workerToScene; }
+ set { workerToScene = value; }
+ }
+
+ ///
+ public virtual void BeginBuild()
+ {
+ savedScenePath = EditorSceneManager.GetActiveScene().path;
+ }
+
+ ///
+ public virtual void EndBuild()
+ {
+ // Restore the previous scene.
+ if (!string.IsNullOrEmpty(savedScenePath))
+ {
+ EditorSceneManager.OpenScene(savedScenePath, OpenSceneMode.Single);
+ }
+ }
+
+ ///
+ public virtual string[] GetScenes(WorkerPlatform workerType)
+ {
+ var scenePath = workerToScene[workerType];
+ var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
+ ProcessScene(scene.name);
+
+ return new[] { scenePath };
+ }
+
+ ///
+ public virtual void BeginPackage(WorkerPlatform workerType, BuildTarget target, Config config, string packagePath)
+ {
+#pragma warning disable 0618
+ UnityPlayerBuilder.GetPackager(workerType, target, config).Prepare(packagePath);
+#pragma warning restore 0618
+ }
+
+ ///
+ /// Override to modify the scene before building.
+ ///
+ /// This is called just after the scene is loaded by .
+ /// The name of the scene to modify
+ public virtual void ProcessScene(string sceneName)
+ {
+#pragma warning disable 0618
+ UnityPlayerBuilder.ProcessScene(sceneName);
+#pragma warning restore 0618
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SingleScenePlayerBuildEvents.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SingleScenePlayerBuildEvents.cs.meta
new file mode 100644
index 0000000..f4b4b9f
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SingleScenePlayerBuildEvents.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 6ccd6b6db6e790c439b39599451a6a8a
+timeCreated: 1491492707
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SpatialZip.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SpatialZip.cs
new file mode 100644
index 0000000..e6094d4
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SpatialZip.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using Improbable.Unity.Editor;
+using Improbable.Unity.Util;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ static class SpatialZip
+ {
+ private const string ZipSubCommand = "file zip";
+
+ internal static void Zip(string spatialCommand, string zipAbsolutePath, string basePath, string subFolder, string filePattern, PlayerCompression useCompression)
+ {
+ var zipFileFullPath = Path.GetFullPath(zipAbsolutePath);
+ var startInfo = new ProcessStartInfo
+ {
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ FileName = spatialCommand,
+ Arguments = ZipArgs(basePath, subFolder, filePattern, zipFileFullPath, useCompression),
+ CreateNoWindow = true
+ };
+
+ var zipProcess = SpatialRunner.RunCommandWithSpatialInThePath(spatialCommand, startInfo);
+
+ var output = zipProcess.StandardOutput.ReadToEnd();
+ var errOut = zipProcess.StandardError.ReadToEnd();
+ zipProcess.WaitForExit();
+ if (zipProcess.ExitCode != 0)
+ {
+ throw new Exception(string.Format("Could not package the folder {0}/{1}. The following error occurred: {2}, {3}\n", basePath, subFolder, output, errOut));
+ }
+ }
+
+ private static string ZipArgs(string basePath, string subFolder, string filePattern, string zipFileFullPath, PlayerCompression useCompression)
+ {
+ filePattern = string.IsNullOrEmpty(filePattern) ? "**" : filePattern;
+ return string.Format("{0} --output=\"{1}\" --basePath=\"{2}\" --relativePath=. \"{3}\" --compression={4}",
+ ZipSubCommand,
+ zipFileFullPath,
+ Path.GetFullPath(basePath),
+ PathUtil.EnsureTrailingSlash(subFolder) + filePattern,
+ useCompression == PlayerCompression.Enabled);
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SpatialZip.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SpatialZip.cs.meta
new file mode 100644
index 0000000..b7162d4
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/SpatialZip.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: baea0b11d5f67f046bcb52129e81ca1e
+timeCreated: 1464278801
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuildMenu.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuildMenu.cs
new file mode 100644
index 0000000..6312cfb
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuildMenu.cs
@@ -0,0 +1,79 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ [InitializeOnLoad]
+ public static class UnityPlayerBuilderMenu
+ {
+ private const string AutopatchMenuItem = "Improbable/Autopatch workers on editor reload";
+ private const string AutoPatchEditorKey = "Improbable.Autopatch.Workers";
+
+ [MenuItem("Improbable/Build/Build deployment workers %#&3")]
+ public static void BuildDeploymentPlayers()
+ {
+ BuildPlayers(UnityPlayerBuilders.DeploymentTarget);
+ }
+
+ [MenuItem("Improbable/Build/Build development workers %#&2")]
+ public static void BuildDevelopmentPlayers()
+ {
+ BuildPlayers(UnityPlayerBuilders.DevelopmentTarget);
+ }
+
+ [MenuItem("Improbable/Build/Clean all workers %#&1")]
+ public static void CleanAllPlayers()
+ {
+ SimpleBuildSystem.CleanPlayers();
+ }
+
+ public static bool IsAutopatchEnabled()
+ {
+ return EditorPrefs.GetBool(AutoPatchEditorKey, defaultValue: false);
+ }
+
+ [MenuItem(AutopatchMenuItem, true)]
+ public static bool ValidateAutoPatch()
+ {
+ // Ensure this is always up-to-date
+ Menu.SetChecked(AutopatchMenuItem, IsAutopatchEnabled());
+ return true;
+ }
+
+ [MenuItem(AutopatchMenuItem)]
+ public static void ToggleAutopatch()
+ {
+ var toggledValue = !IsAutopatchEnabled();
+ EditorPrefs.SetBool(AutoPatchEditorKey, toggledValue);
+
+ Debug.LogFormat("Auto-patching workers is {0}", toggledValue ? "enabled" : "disabled");
+
+ if (IsAutopatchEnabled())
+ {
+ ReloadAssemblies.OnScriptsReloaded();
+ }
+ }
+
+ private static void BuildPlayers(string target)
+ {
+ var stopWatch = new System.Diagnostics.Stopwatch();
+ stopWatch.Start();
+
+ try
+ {
+ SimpleBuildSystem.WorkersToBuild = SimpleBuildSystem.AllWorkerTypes;
+ SimpleBuildSystem.Target = target;
+ SimpleBuildSystem.Build();
+ }
+ finally
+ {
+ SimpleBuildSystem.WorkersToBuild = null;
+ SimpleBuildSystem.Target = null;
+
+ stopWatch.Stop();
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuildMenu.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuildMenu.cs.meta
new file mode 100644
index 0000000..886f595
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuildMenu.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5925218dcb332be4696802bd391017b5
+timeCreated: 1444833358
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilder.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilder.cs
new file mode 100644
index 0000000..494f60d
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilder.cs
@@ -0,0 +1,262 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Improbable.Unity.Assets;
+using Improbable.Unity.EditorTools.Util;
+using Improbable.Unity.Util;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ public class UnityPlayerBuilder
+ {
+ /// This is obsolete, please see to customize player packaging.
+ [Obsolete("Obsolete in 10.3.0. Please see IPlayerBuildEvents for information about customizing player packaging.")]
+#pragma warning disable 0618
+ public static Func GetPackager = GetDefaultPackager;
+#pragma warning restore 0618
+
+ /// This is obsolete, please see to customize scene processing.
+ [Obsolete("Obsolete in 10.3.0. Please see IPlayerBuildEvents for information about customizing scene loading.")]
+ public static Action ProcessScene = sceneName => { };
+
+ public readonly BuildTarget BuildTarget;
+ private readonly Config config;
+ public readonly WorkerPlatform WorkerType;
+ private readonly BuildOptions options;
+ private readonly PlatformData platformData;
+
+ internal IPlayerBuildEvents PlayerBuildEvents { get; set; }
+
+ public UnityPlayerBuilder(WorkerPlatform workerType, string targetString, Config config)
+ {
+ WorkerType = workerType;
+ BuildTarget = ToRuntimePlatform(targetString);
+ this.config = config;
+ options = GenerateFlag(config.FlagsForPlatform(targetString));
+ platformData = CreatePlatformData(BuildTarget);
+ }
+
+ private static BuildTarget ToRuntimePlatform(string platform)
+ {
+ if (platform.Contains("?"))
+ {
+ platform = platform.Substring(0, platform.IndexOf("?", StringComparison.Ordinal));
+ }
+
+ if (platform.ToLower() != "current")
+ {
+ var value = (BuildTarget) Enum.Parse(typeof(BuildTarget), platform);
+#if UNITY_2017_3_OR_NEWER
+#pragma warning disable 618
+ if (value == BuildTarget.StandaloneOSXIntel64)
+ {
+ Debug.LogWarningFormat("{0} is deprecated and will be removed. Please update {1} to use {2} instead.", BuildTarget.StandaloneOSXIntel64, UnityPlayerBuilders.PlayerConfigurationFilePath, BuildTarget.StandaloneOSX);
+ value = BuildTarget.StandaloneOSX;
+ }
+#pragma warning restore 618
+#endif
+ return value;
+ }
+
+ return CurrentPlatform();
+ }
+
+ internal static BuildTarget CurrentPlatform()
+ {
+ switch (Application.platform)
+ {
+ case RuntimePlatform.WindowsEditor:
+ return BuildTarget.StandaloneWindows;
+ case RuntimePlatform.OSXEditor:
+#if UNITY_2017_3_OR_NEWER
+ return BuildTarget.StandaloneOSX;
+#else
+ return BuildTarget.StandaloneOSXIntel64;
+#endif
+ case RuntimePlatform.LinuxEditor:
+ return BuildTarget.StandaloneLinux64;
+ default:
+ throw new System.ComponentModel.InvalidEnumArgumentException(string.Format("Unsupported runtime platform {0}", Application.platform));
+ }
+ }
+
+ public static string PlayerBuildScratchDirectory
+ {
+ get { return Path.GetFullPath(Path.Combine("build", "worker")); }
+ }
+
+ public static string PlayerBuildDirectory
+ {
+ get { return PathUtil.Combine(Directory.GetCurrentDirectory(), EditorPaths.AssetDatabaseDirectory, "worker"); }
+ }
+
+ private string ExecutableName
+ {
+ get
+ {
+ return string.Format("{0}@{1}{2}", WorkerType, platformData.BuildContext,
+ platformData.ExecutableExtension);
+ }
+ }
+
+ private string PackageName
+ {
+ get { return string.Format("{0}@{1}", WorkerType, platformData.BuildContext); }
+ }
+
+ private string PackagePath
+ {
+ get { return Path.Combine(PlayerBuildScratchDirectory, PackageName); }
+ }
+
+ internal string AssemblyDirectory
+ {
+ get
+ {
+ switch (BuildTarget)
+ {
+#if UNITY_2017_3_OR_NEWER
+ case BuildTarget.StandaloneOSX:
+#else
+ case BuildTarget.StandaloneOSXIntel64:
+#endif
+ {
+ return PathUtil.Combine(PackagePath, string.Format("{0}.app", PackageName), "Contents", "Resources", "Data", "Managed");
+ }
+ default:
+ {
+ return PathUtil.Combine(PackagePath, string.Format("{0}_Data", PackageName), "Managed");
+ }
+ }
+ }
+ }
+
+ private string ZipPath
+ {
+ get { return Path.Combine(PlayerBuildDirectory, PackageName); }
+ }
+
+ private string BuildConfigComment
+ {
+ get
+ {
+ return string.Format("WorkerType={0};BuildTarget={1};EmbedAssets={2};BuildOptions={3}", WorkerType,
+ BuildTarget, config.Assets == AssetDatabaseStrategy.Local.ToString(), options);
+ }
+ }
+
+ public bool AssemblyDirectoryEmpty()
+ {
+ return !Directory.Exists(AssemblyDirectory) || !Directory.GetFileSystemEntries(AssemblyDirectory).Any();
+ }
+
+ public void PatchAssembly(string sourcePath)
+ {
+ if (!Directory.Exists(AssemblyDirectory))
+ {
+ return;
+ }
+
+ var fileName = Path.GetFileName(sourcePath);
+ var targetPath = Path.Combine(AssemblyDirectory, fileName);
+
+ File.Copy(sourcePath, targetPath, overwrite: true);
+ }
+
+#pragma warning disable 0618
+ [Obsolete("Obsolete in 10.3.0. Please see IPlayerBuildEvents for information about customizing player packaging.")]
+ public static IPackager GetDefaultPackager(WorkerPlatform workerType, BuildTarget buildTarget, Config config)
+#pragma warning restore 0618
+ {
+ return new SimplePackager();
+ }
+
+ public void Clean()
+ {
+ UnityPathUtil.EnsureDirectoryRemoved(PackagePath);
+ UnityPathUtil.EnsureFileRemoved(ZipPath + ".zip");
+ }
+
+ [Obsolete("Obsolete in 10.3.0. This will be removed in a future version.")]
+ public void PackagePlayer(string spatialPath, PlayerCompression useCompression)
+ {
+ PackagePlayer(PlayerBuildEvents, spatialPath, useCompression);
+ }
+
+ internal void PackagePlayer(IPlayerBuildEvents events, string spatialPath, PlayerCompression useCompression)
+ {
+ events.BeginPackage(WorkerType, BuildTarget, config, PackagePath);
+ SpatialZip.Zip(spatialPath, ZipPath, PackagePath, ".", "**", useCompression);
+ }
+
+ private static PlatformData CreatePlatformData(BuildTarget buildTarget)
+ {
+ switch (buildTarget)
+ {
+ case BuildTarget.StandaloneWindows:
+ return new PlatformData("Managed", "Windows", "_Data", ".exe");
+ case BuildTarget.StandaloneWindows64:
+ return new PlatformData("Managed", "Windows", "_Data", ".exe");
+#if UNITY_2017_3_OR_NEWER
+ case BuildTarget.StandaloneOSX:
+#else
+ case BuildTarget.StandaloneOSXIntel64:
+#endif
+ return new PlatformData("Contents/Data/Managed", "Mac", ".app", "");
+ case BuildTarget.StandaloneLinux64:
+ return new PlatformData("Managed", "Linux", "_Data", "");
+ case BuildTarget.iOS:
+ return new PlatformData("Data/Managed", "iOS", "", "");
+ }
+
+ throw new ArgumentException("Unsupported platform " + buildTarget);
+ }
+
+ internal void BuildPlayer()
+ {
+ PathUtil.EnsureDirectoryExists(PlayerBuildDirectory);
+ PathUtil.EnsureDirectoryExists(PlayerBuildScratchDirectory);
+
+ var scenes = PlayerBuildEvents.GetScenes(WorkerType);
+
+ var tempExecutablePath = Path.Combine(PackagePath, ExecutableName);
+
+ var playerOptions = new BuildPlayerOptions { target = BuildTarget, locationPathName = tempExecutablePath, options = options, scenes = scenes };
+ var buildErrorMessage = BuildPipeline.BuildPlayer(playerOptions);
+ if (!string.IsNullOrEmpty(buildErrorMessage))
+ {
+ throw new ApplicationException(string.Format("Failed to build player {0} due to {1}", BuildConfigComment,
+ buildErrorMessage));
+ }
+
+ Debug.LogFormat("Built player {0} into {1}", BuildConfigComment, PackagePath);
+ }
+
+ private BuildOptions GenerateFlag(IEnumerable flagList)
+ {
+ return flagList.Aggregate((a, b) => a | b);
+ }
+ }
+
+ internal class PlatformData
+ {
+ public readonly string AssemblyPathWithinPackage;
+ public readonly string BuildContext;
+ public readonly string DataFolderExtension;
+ public readonly string ExecutableExtension;
+
+ public PlatformData(string assemblyPathWithinPackage, string buildContext, string dataFolderExtension,
+ string executableExtension)
+ {
+ AssemblyPathWithinPackage = assemblyPathWithinPackage;
+ BuildContext = buildContext;
+ DataFolderExtension = dataFolderExtension;
+ ExecutableExtension = executableExtension;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilder.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilder.cs.meta
new file mode 100644
index 0000000..42f9228
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilder.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 659e79647d9ba1f438631d78b45f717e
+timeCreated: 1444833358
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilders.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilders.cs
new file mode 100644
index 0000000..51dcd4c
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilders.cs
@@ -0,0 +1,183 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using Improbable.Unity.Editor.Addons;
+using Improbable.Unity.Editor.Core;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Build
+{
+ public static class UnityPlayerBuilders
+ {
+ public static readonly string PlayerConfigurationFilePath = Path.Combine(Application.dataPath, "player-build-config.json");
+
+ internal const string DeploymentTarget = "deployment";
+ internal const string DevelopmentTarget = "development";
+
+ [Obsolete("GlobalConfig is deprecated and will be removed in an upcoming SpatialOS version.")]
+ public static GlobalConfig GlobalConfig
+ {
+ get { return LoadConfiguation().Global; }
+ }
+
+ public static IList DeploymentPlayerBuilders(IList selectedWorkerTypes)
+ {
+ return ConfiguredBuilders(LoadConfiguation().Deploy, selectedWorkerTypes);
+ }
+
+ public static IList DevelopmentPlayerBuilders(IList selectedWorkerTypes)
+ {
+ return ConfiguredBuilders(LoadConfiguation().Develop, selectedWorkerTypes);
+ }
+
+ /// Retrieve plugin for current platform.
+ /// This is deprecated in favor of the spatialos_worker_packages.json
+ [Obsolete("RetrievePluginForCurrentPlatform is deprecated. Please use the spatialos_worker_packages.json to download the CoreSdkDll plugins.")]
+ public static void RetrievePluginForCurrentPlatform() { }
+
+ /// Retrieves plugins for deployment players.
+ /// This is deprecated in favor of the spatialos_worker_packages.json
+ [Obsolete("RetrievePlayerPlugins is deprecated. Please use the spatialos_worker_packages.json to download the CoreSdkDll plugins.")]
+ public static void RetrievePlayerPlugins(IList buildTargets) { }
+
+ public static void BuildDeploymentPlayers(IList selectedWorkerTypes)
+ {
+ BuildPlayers(DeploymentPlayerBuilders(selectedWorkerTypes), selectedWorkerTypes, PlayerCompression.Enabled);
+ }
+
+ public static void BuildDevelopmentPlayers(IList selectedWorkerTypes)
+ {
+ BuildPlayers(DevelopmentPlayerBuilders(selectedWorkerTypes), selectedWorkerTypes, PlayerCompression.Disabled);
+ }
+
+ private static void BuildPlayers(IList playerBuilders, IList selectedWorkerTypes, PlayerCompression compression)
+ {
+ PrepareWorkerAssembly(selectedWorkerTypes);
+
+ var playerBuildEvents = SimpleBuildSystem.CreatePlayerBuildEventsAction();
+
+ var currentBuildTarget = EditorUserBuildSettings.activeBuildTarget;
+ try
+ {
+ EditorApplication.LockReloadAssemblies();
+ playerBuildEvents.BeginBuild();
+
+ var exceptions = 0;
+ var threads = new List();
+
+ var spatialPath = SpatialCommand.SpatialPath;
+
+ foreach (var playerBuilder in playerBuilders)
+ {
+ playerBuilder.PlayerBuildEvents = playerBuildEvents;
+
+ playerBuilder.Clean();
+ playerBuilder.BuildPlayer();
+ var builder = playerBuilder;
+
+ var thread = new Thread(() =>
+ {
+ try
+ {
+#pragma warning disable 0618 // Type or member is obsolete
+ builder.PackagePlayer(spatialPath, compression);
+#pragma warning restore 0618 // Type or member is obsolete
+ }
+ catch (Exception e)
+ {
+ Debug.LogError(e);
+ Interlocked.Increment(ref exceptions);
+ throw;
+ }
+ });
+ thread.Start();
+ threads.Add(thread);
+ }
+
+ try
+ {
+ for (var i = 0; i < threads.Count; ++i)
+ {
+ EditorUtility.DisplayProgressBar("Packaging players", "Packaging and zipping players. This may take a while.", (float) i / threads.Count);
+ threads[i].Join();
+ }
+ }
+ finally
+ {
+ EditorUtility.ClearProgressBar();
+ }
+
+ if (exceptions > 0)
+ {
+ throw new Exception(string.Format("Building {0} of the players failed. Please look at logs.", exceptions));
+ }
+
+ Debug.Log("Finished building players.");
+ }
+ finally
+ {
+ EditorApplication.UnlockReloadAssemblies();
+ playerBuildEvents.EndBuild();
+#pragma warning disable 618
+ EditorUserBuildSettings.SwitchActiveBuildTarget(currentBuildTarget);
+#pragma warning restore 618
+ }
+ }
+
+ private static IList ConfiguredBuilders(Enviroment env, IList selectedWorkerTypes)
+ {
+ var players = new List();
+
+ foreach (var t in selectedWorkerTypes)
+ {
+ switch (t)
+ {
+ case WorkerTypeUtils.UnityClientType:
+ players.AddRange(ToPlatformBuilders(WorkerPlatform.UnityClient, env.UnityClient));
+ break;
+ case WorkerTypeUtils.UnityWorkerType:
+ players.AddRange(ToPlatformBuilders(WorkerPlatform.UnityWorker, env.UnityWorker));
+ break;
+ default:
+ throw new InvalidOperationException(string.Format("Unknown player type '{0}'", t));
+ }
+ }
+
+ return players;
+ }
+
+ private static IList ToPlatformBuilders(WorkerPlatform platform, Config config)
+ {
+ if (config == null)
+ {
+ return new List();
+ }
+
+ return config.Targets.Select(configTarget => new UnityPlayerBuilder(platform, configTarget, config)).ToList();
+ }
+
+ private static PlayerBuildConfiguation LoadConfiguation()
+ {
+ if (File.Exists(PlayerConfigurationFilePath))
+ {
+ return JsonUtility.FromJson(File.ReadAllText(PlayerConfigurationFilePath));
+ }
+
+ return DefaultPlayerBuildConfiguration.Generate();
+ }
+
+ private static void PrepareWorkerAssembly(IList selectedWorkerTypes)
+ {
+ var workerTypeArguments = string.Join(" ", selectedWorkerTypes.Distinct().ToArray());
+ var command = SpatialCommand.SpatialPath;
+ var arguments = "build build-config " + workerTypeArguments;
+ var applicationRootPath = Path.GetFullPath(Path.Combine(Application.dataPath, "../../.."));
+ SpatialOsEditor.RunProcess(command, arguments, applicationRootPath);
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilders.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilders.cs.meta
new file mode 100644
index 0000000..515bdb6
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Build/UnityPlayerBuilders.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: bedb4a8ce630e724e8fde8214892191f
+timeCreated: 1444833361
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Configuration.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Configuration.meta
new file mode 100644
index 0000000..217a240
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Configuration.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 5c4d6f53f89e4ac48a6d3a8fed508b06
+folderAsset: yes
+timeCreated: 1479735828
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Configuration/ConfigNames.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Configuration/ConfigNames.cs
new file mode 100644
index 0000000..9012cd0
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Configuration/ConfigNames.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+
+namespace Improbable.Editor.Configuration
+{
+ ///
+ /// Provides command line arguments to control editor-related operations.
+ ///
+ public static class ConfigNames
+ {
+ ///
+ /// The name(s) of the worker types that should be built.
+ ///
+ public const string BuildWorkerTypes = "buildWorkerTypes";
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Configuration/ConfigNames.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Configuration/ConfigNames.cs.meta
new file mode 100644
index 0000000..0f8dae3
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Configuration/ConfigNames.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 8dbb726e9f91408478a72b5e8d2f0b84
+timeCreated: 1479735828
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core.meta
new file mode 100644
index 0000000..de865fd
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: e4a940bf280bee34085e2e104f6ea514
+folderAsset: yes
+timeCreated: 1483974837
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/DefaultWorkerProvider.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/DefaultWorkerProvider.cs
new file mode 100644
index 0000000..6ea738b
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/DefaultWorkerProvider.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Improbable.Unity.Editor.Core;
+using Improbable.Unity.EditorTools.Build;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Core
+{
+ ///
+ /// Provides workers from the worker.json files scanned from the worker's folder.
+ ///
+ internal class DefaultWorkerProvider : IWorkerProvider
+ {
+ private IList workers;
+
+ private const string WorkerNamePrefix = "spatialos.";
+ private const string WorkerNameSuffix = ".worker.json";
+
+ public IList GetWorkers()
+ {
+ if (workers != null)
+ {
+ return workers;
+ }
+
+ const string searchPattern = WorkerNamePrefix + "*" + WorkerNameSuffix;
+ try
+ {
+ var workerFiles = Directory.GetFiles(SpatialOsEditor.WorkerRootDir, searchPattern).Select(ExtractPlayerName);
+ workers = workerFiles.Select(f => new SpatialOsWorker(ExtractPlayerName(f))).ToList();
+ return workers;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogException(ex);
+ return new List();
+ }
+ }
+
+ private static string ExtractPlayerName(string fileName)
+ {
+ var workerName = Path.GetFileName(fileName);
+ if (workerName != null)
+ {
+ workerName = workerName.Replace(WorkerNamePrefix, string.Empty);
+ workerName = workerName.Replace(WorkerNameSuffix, string.Empty);
+ return workerName;
+ }
+
+ return fileName;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/DefaultWorkerProvider.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/DefaultWorkerProvider.cs.meta
new file mode 100644
index 0000000..06b71d2
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/DefaultWorkerProvider.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: bea1825da1427c14ba77dae72301967b
+timeCreated: 1494259756
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddon.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddon.cs
new file mode 100644
index 0000000..40f96c0
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddon.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+namespace Improbable.Unity.Editor.Core
+{
+ ///
+ /// Implements a SpatialOS Addon for the Unity Editor.
+ ///
+ public interface ISpatialOsEditorAddon
+ {
+ ///
+ /// The name of the editor, as displayed to the user.
+ ///
+ string Name { get; }
+
+ ///
+ /// The name of the vendor, as displayed to the user.
+ ///
+ string Vendor { get; }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddon.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddon.cs.meta
new file mode 100644
index 0000000..95b089f
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddon.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: d101de0eec61b824480aab20bc74bdb2
+timeCreated: 1484056898
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonBuild.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonBuild.cs
new file mode 100644
index 0000000..3dd2bf8
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonBuild.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using UnityEngine;
+
+namespace Improbable.Unity.Editor.Core
+{
+ ///
+ /// Allows an addon to present a user interface.
+ ///
+ public interface ISpatialOsEditorAddonBuild
+ {
+ ///
+ /// Called by the when the addon is visible.
+ ///
+ void OnDevGui(Rect rect);
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonBuild.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonBuild.cs.meta
new file mode 100644
index 0000000..399f6fa
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonBuild.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 2554d6c3b0c1fbe4ca70f41a076b5466
+timeCreated: 1484299796
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonSettings.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonSettings.cs
new file mode 100644
index 0000000..74f50f6
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonSettings.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using UnityEngine;
+
+namespace Improbable.Unity.Editor.Core
+{
+ ///
+ /// Allows an addon to present an interface for modifying its settings.
+ ///
+ public interface ISpatialOsEditorAddonSettings
+ {
+ ///
+ /// Called by the when the addon needs to render settings.
+ ///
+ void OnSettingsGui(Rect rect);
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonSettings.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonSettings.cs.meta
new file mode 100644
index 0000000..8e3754a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonSettings.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 33798866714d7d8488be1c408334875b
+timeCreated: 1484299796
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonToolbar.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonToolbar.cs
new file mode 100644
index 0000000..cd9f7dc
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonToolbar.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using UnityEngine;
+
+namespace Improbable.Unity.Editor.Core
+{
+ ///
+ /// Allows an addon to present an interface in the development toolbar.
+ ///
+ public interface ISpatialOsEditorAddonToolbar
+ {
+ ///
+ /// Called by the when the addon needs to render to the toolbar.
+ ///
+ void OnToolbarGui(Rect rect);
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonToolbar.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonToolbar.cs.meta
new file mode 100644
index 0000000..4e20ec4
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/ISpatialOsEditorAddonToolbar.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 89e03c9a26bb4624f904e835459de6f7
+timeCreated: 1484299797
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SharedGuiContent.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SharedGuiContent.cs
new file mode 100644
index 0000000..acb08a8
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SharedGuiContent.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Editor.Core
+{
+ ///
+ /// Gui-related content to provide a cohesive user experience.
+ ///
+ public class SharedGuiContent
+ {
+ ///
+ /// Draws a label that is sized to its content.
+ ///
+ public GUIStyle MinimalLabelStyle
+ {
+ get { return new GUIStyle(GUI.skin.label) { stretchWidth = false }; }
+ }
+
+ ///
+ /// A foldout header with bold text.
+ ///
+ public GUIStyle BoldFoldout
+ {
+ get { return new GUIStyle(EditorStyles.foldout) { fontStyle = FontStyle.Bold }; }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SharedGuiContent.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SharedGuiContent.cs.meta
new file mode 100644
index 0000000..8447797
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SharedGuiContent.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ebfa2a05eda227a4084af6ad872551f6
+timeCreated: 1484056898
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsAuxWindow.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsAuxWindow.cs
new file mode 100644
index 0000000..9892d4e
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsAuxWindow.cs
@@ -0,0 +1,168 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Linq;
+using Improbable.Unity.EditorTools;
+using Improbable.Unity.EditorTools.Util;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.Editor.Core
+{
+ [Serializable]
+ class SpatialOsAuxWindow : EditorWindow
+ {
+ [SerializeField] private string addonTitle;
+ [SerializeField] private string typeName;
+ [SerializeField] private AddonUiStateDictinoary addonState;
+
+ private ISpatialOsEditorAddon[] addons;
+ private Type addonType;
+
+ SpatialOsAuxWindow()
+ {
+ titleContent = new GUIContent("SpatialOS");
+ }
+
+ public void OnEnable()
+ {
+ if (addonState == null)
+ {
+ addonState = new AddonUiStateDictinoary();
+ }
+
+ if (typeName != null)
+ {
+ var type = Type.GetType(typeName, false);
+ if (type != null)
+ {
+ SetAddonType(type, addonTitle);
+ }
+ else
+ {
+ Debug.LogErrorFormat("Could not find type {0}", typeName);
+ typeName = null;
+ addons = null;
+ }
+
+ Repaint();
+ }
+ }
+
+ public void SetAddonType(Type type, string newAddonTitle)
+ {
+ addonType = type;
+ addons = SpatialOsEditor.Addons().Where(type.IsInstanceOfType).ToArray();
+ typeName = type.FullName;
+ addonTitle = newAddonTitle;
+ titleContent = new GUIContent(newAddonTitle);
+ Repaint();
+ }
+
+ public void OnGUI()
+ {
+ if (SpatialOsWindow.SharedContent == null)
+ {
+ return;
+ }
+
+ if (addons == null || addons.Length == 0)
+ {
+ using (new EditorGUILayout.VerticalScope())
+ {
+ EditorGUILayout.HelpBox("Please select an item in the SpatialOS window.", MessageType.Info);
+ if (GUILayout.Button("Show SpatialOS window"))
+ {
+ GetWindow().Show();
+ }
+ }
+
+ return;
+ }
+
+ AddonUiState s;
+ if (!addonState.TryGetValue(typeName, out s))
+ {
+ addonState[typeName] = s = new AddonUiState();
+ }
+
+ if (s.Selected == null || s.Selected.Length != addons.Length)
+ {
+ s.Selected = Enumerable.Repeat(true, addons.Length).ToArray();
+ }
+
+ if (s.Rects == null || s.Rects.Length != addons.Length)
+ {
+ s.Rects = new Rect[addons.Length];
+ }
+
+ var rect = new Rect(0, 0, position.width, position.height);
+
+ using (var scroller = new EditorGUILayout.ScrollViewScope(s.ScrollPosition, false, false))
+ using (new EditorGUILayout.VerticalScope())
+ {
+ for (var index = 0; index < addons.Length; index++)
+ {
+ using (new EditorGUILayout.VerticalScope())
+ {
+ EditorGUILayout.Separator();
+ s.Selected[index] = EditorGUILayout.Foldout(s.Selected[index], addons[index].Name,
+ SpatialOsWindow.SharedContent.BoldFoldout);
+ if (s.Selected[index])
+ {
+ if (addonType.IsAssignableFrom(typeof(ISpatialOsEditorAddonBuild)))
+ {
+ ((ISpatialOsEditorAddonBuild) addons[index]).OnDevGui(rect);
+ }
+ else if (addonType.IsAssignableFrom(typeof(ISpatialOsEditorAddonSettings)))
+ {
+ ((ISpatialOsEditorAddonSettings) addons[index]).OnSettingsGui(rect);
+ }
+ else
+ {
+ EditorGUILayout.HelpBox(
+ string.Format("Can't draw addons of type {0}", addons[index].GetType()),
+ MessageType.Warning);
+ }
+ }
+
+ EditorGUILayout.Separator();
+ }
+
+ if (Event.current.type == EventType.Repaint)
+ {
+ s.Rects[index] = GUILayoutUtility.GetLastRect();
+ }
+ }
+
+ s.ScrollPosition = scroller.scrollPosition;
+ }
+
+
+ Handles.BeginGUI();
+ Handles.color = new Color(0.5f, 0.5f, 0.5f, 1f);
+
+ for (var index = 1; index < addons.Length; index++)
+ {
+ var yMin = s.Rects[index].yMin - s.ScrollPosition.y;
+ Handles.DrawLine(new Vector3(0, yMin), new Vector3(s.Rects[index].xMax, yMin));
+ }
+
+ Handles.EndGUI();
+ }
+
+ [Serializable]
+ private class AddonUiState
+ {
+ public bool[] Selected;
+ public Vector2 ScrollPosition;
+ public Rect[] Rects;
+ }
+
+ ///
+ /// Unity can't serialize generics directly.
+ ///
+ [Serializable]
+ private class AddonUiStateDictinoary : SerializableDictionary { }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsAuxWindow.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsAuxWindow.cs.meta
new file mode 100644
index 0000000..d7f45de
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsAuxWindow.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 55b72e45cb1ddf54eb61db17215a3bea
+timeCreated: 1494404803
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsEditor.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsEditor.cs
new file mode 100644
index 0000000..ce58309
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsEditor.cs
@@ -0,0 +1,223 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using Improbable.Unity.Core;
+using Improbable.Unity.EditorTools.Build;
+using Improbable.Unity.EditorTools.Core;
+using Improbable.Unity.Util;
+using UnityEditor;
+using UnityEngine;
+using Debug = UnityEngine.Debug;
+
+namespace Improbable.Unity.Editor.Core
+{
+ ///
+ /// Provides SpatialOS-specific functionality for the Unity Editor.
+ ///
+ [InitializeOnLoad]
+ public static class SpatialOsEditor
+ {
+ private static readonly Dictionary RegisteredAddons = new Dictionary();
+
+ private static ProjectDescriptor projectDescriptor;
+
+ private static IWorkerProvider workerProvider;
+ private static WorkerSelection workerSelection;
+
+ private static readonly string WorkerSelectionAssetPath;
+
+ ///
+ /// The absolute path of the worker.
+ ///
+ public static string WorkerRootDir { get; private set; }
+
+ ///
+ /// The absolute path of the SpatialOS project.
+ ///
+ public static string ApplicationRootDir { get; private set; }
+
+ ///
+ /// Contains information about the SpatialOS project as a whole.
+ ///
+ public static ProjectDescriptor ProjectDescriptor
+ {
+ get { return projectDescriptor ?? (projectDescriptor = ProjectDescriptor.Load()); }
+ }
+
+ internal static IWorkerProvider WorkerProvider
+ {
+ get { return workerProvider ?? (workerProvider = new DefaultWorkerProvider()); }
+ set { workerProvider = value; }
+ }
+
+ ///
+ /// Manages the selection of available workers.
+ ///
+ public static WorkerSelection WorkerSelection
+ {
+ get
+ {
+ if (workerSelection != null)
+ {
+ return workerSelection;
+ }
+
+ workerSelection = new WorkerSelection();
+ InitializeWorkerSelection(workerSelection);
+ return workerSelection;
+ }
+ }
+
+ ///
+ /// Gets an instance of a specific addon.
+ ///
+ /// If the addon is not registered.
+ /// >
+ public static TAddon GetAddon() where TAddon : ISpatialOsEditorAddon
+ {
+ return (TAddon) RegisteredAddons[typeof(TAddon)];
+ }
+
+ ///
+ /// Registers an instance
+ ///
+ /// If the addon is already registered.
+ public static void RegisterAddon(ISpatialOsEditorAddon addon)
+ {
+ if (RegisteredAddons.ContainsKey(addon.GetType()))
+ {
+ throw new InvalidOperationException(string.Format("{0} is already registered", addon.GetType()));
+ }
+
+ RegisteredAddons[addon.GetType()] = addon;
+ }
+
+ ///
+ /// Returns a list of all currently-registered addons.
+ ///
+ public static IList Addons()
+ {
+ return RegisteredAddons.Select(kv => kv.Value).ToList();
+ }
+
+ ///
+ /// Workers that exist within the current Unity project.
+ ///
+ public static IList Workers
+ {
+ get { return WorkerProvider.GetWorkers(); }
+ }
+
+ ///
+ /// Temporary solution for running external processes.
+ ///
+ public static void RunProcess(string command, string arguments, string workingDir)
+ {
+ var process = new Process
+ {
+ StartInfo =
+ {
+ FileName = command,
+ Arguments = arguments,
+ WorkingDirectory = workingDir,
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ }
+ };
+
+ try
+ {
+ process.Start();
+ process.WaitForExit();
+
+ var output = process.StandardOutput.ReadToEnd();
+ var error = process.StandardError.ReadToEnd();
+
+ // This is a workaround as toolbelt's regular output gets piped to stderror for some reason.
+ LogAsSeparateLines(output + "\n" + error, (process.ExitCode != 0) ? LogType.Error : LogType.Log);
+ if (process.ExitCode != 0)
+ {
+ throw new Exception(string.Format("Process call {0} {1} failed with exit code {2}.", command, arguments, process.ExitCode));
+ }
+ }
+ finally
+ {
+ process.Close();
+ }
+ }
+
+ private static void LogAsSeparateLines(string input, LogType logType)
+ {
+ var lines = input.Split('\n').Select(s => s.Trim()).Where(line => !string.IsNullOrEmpty(line));
+ foreach (var line in lines)
+ {
+#pragma warning disable 618
+ Debug.logger.LogFormat(logType, "{0}", line);
+#pragma warning restore 618
+ }
+ }
+
+ ///
+ /// Temporary solution for running external processes.
+ ///
+ public static void RunPausedProcess(string command, string arguments, string workingDir)
+ {
+ SpatialRunner.RunPausedProcess(command, arguments, workingDir);
+ }
+
+ static SpatialOsEditor()
+ {
+ WorkerRootDir = Path.GetFullPath(PathUtil.Combine(Application.dataPath, ".."));
+ ApplicationRootDir = Path.GetFullPath(PathUtil.Combine(WorkerRootDir, "..", ".."));
+ WorkerSelectionAssetPath = PathUtil.Combine(WorkerRootDir, "build", "selected_workers.txt");
+ }
+
+ ///
+ /// Read the names of workers from a text file, one worker name per line. Empty lines are ignored.
+ ///
+ ///
+ /// This exists because we want to store these per-unity project, so the filesystem is the best place.
+ /// EditorPrefs is global to all Unity instances.
+ ///
+ private static void InitializeWorkerSelection(WorkerSelection selection)
+ {
+ // Default to selecting all workers.
+ IEnumerable toSelect = Workers;
+
+ // Load from disk and reconcile the previous selection against available workers.
+ if (File.Exists(WorkerSelectionAssetPath))
+ {
+ try
+ {
+ var sourceText = File.ReadAllText(WorkerSelectionAssetPath);
+ var previouslySelected = sourceText.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
+ toSelect = Workers.Where(w => previouslySelected.Contains(w.Name));
+ }
+ catch { }
+ }
+
+ // Restore the selection state.
+ foreach (var worker in toSelect)
+ {
+ selection.SelectWorker(worker, true);
+ }
+
+ // Listen for selections events so we can save the current selection to disk.
+ selection.OnWorkerSelectionChanged += worker =>
+ {
+ try
+ {
+ var text = string.Join("\n", workerSelection.SelectedWorkers.Select(w => w.Name).ToArray());
+ File.WriteAllText(WorkerSelectionAssetPath, text);
+ }
+ catch { }
+ };
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsEditor.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsEditor.cs.meta
new file mode 100644
index 0000000..f230f37
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsEditor.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 42eef98b867d6204488acd2ed350b7fa
+timeCreated: 1481555891
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWindow.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWindow.cs
new file mode 100644
index 0000000..3a6e21e
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWindow.cs
@@ -0,0 +1,154 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Text.RegularExpressions;
+using Improbable.Editor.Core;
+using Improbable.Unity.Editor.Core;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools
+{
+ ///
+ /// Provides a user interface to common SpatialOS-related development commands.
+ ///
+ [Serializable]
+ [InitializeOnLoad]
+ public class SpatialOsWindow : EditorWindow
+ {
+ private const string MinVersion = "5.6.0";
+ private const string MaxVersion = "2017.3.0";
+
+ private static SharedGuiContent sharedContent;
+ private static string versionMessage;
+
+ private GUIStyle boldStyle;
+ private GUIStyle selectionGridStyle;
+
+ private GUIStyle feedbackButtonStyle;
+
+ private Rect supportButtonRect;
+
+ [MenuItem("Window/SpatialOS")]
+ [MenuItem("Improbable/SpatialOS Window")]
+ private static void ShowWindow()
+ {
+ var instance = GetWindow();
+ instance.titleContent = new GUIContent("SpatialOS");
+ instance.minSize = new Vector2(320, 24);
+ }
+
+ ///
+ /// Accesses common GUI system content and helpers.
+ ///
+ public static SharedGuiContent SharedContent
+ {
+ get { return sharedContent ?? (sharedContent = new SharedGuiContent()); }
+ }
+
+ public void OnEnable()
+ {
+ try
+ {
+ long version = ConvertUnityVersionToNumber(Application.unityVersion);
+ long min = ConvertUnityVersionToNumber(MinVersion);
+ long max = ConvertUnityVersionToNumber(MaxVersion);
+
+ if (version < min)
+ {
+ versionMessage = string.Format("Your Unity version: {0}\nEarliest tested: {1}.\nPlease use at least {1} for the best experience.", Application.unityVersion, MinVersion);
+ }
+ else if (version > max)
+ {
+ versionMessage = string.Format("Your Unity version: {0}\nLatest tested: {1}.\nThings should work fine, but you may find issues.", Application.unityVersion, MaxVersion);
+ }
+ }
+ catch (Exception)
+ {
+ versionMessage = string.Format("Unity {0} may be untested with the SpatialOS for Unity SDK.", Application.unityVersion);
+ }
+
+ if (!string.IsNullOrEmpty(versionMessage))
+ {
+ EditorWindow.GetWindow().minSize = new Vector2(320, 72);
+ }
+ }
+
+ public void OnDestroy() { }
+
+ public void OnGUI()
+ {
+ InitializeOnce();
+
+ using (new EditorGUILayout.VerticalScope())
+ {
+ if (!string.IsNullOrEmpty(versionMessage))
+ {
+ EditorGUILayout.HelpBox(versionMessage, MessageType.Warning);
+ }
+
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandWidth(false)))
+ {
+ EditorGUILayout.SelectableLabel(SpatialOsEditor.ProjectDescriptor.SdkVersion, SharedContent.MinimalLabelStyle);
+ EditorGUILayout.SelectableLabel(SpatialOsEditor.ProjectDescriptor.Name, SharedContent.MinimalLabelStyle);
+ }
+
+ if (GUILayout.Button("Build"))
+ {
+ EditorWindow.GetWindow().SetAddonType(typeof(ISpatialOsEditorAddonBuild), "Build");
+ }
+
+ if (GUILayout.Button("Settings"))
+ {
+ EditorWindow.GetWindow().SetAddonType(typeof(ISpatialOsEditorAddonSettings), "Settings");
+ }
+
+ if (GUILayout.Button("Support"))
+ {
+ PopupWindow.Show(supportButtonRect, new SupportPopup());
+ }
+
+ if (Event.current.type == EventType.Repaint)
+ {
+ supportButtonRect = GUILayoutUtility.GetLastRect();
+ }
+ }
+ }
+ }
+
+ private void InitializeOnce()
+ {
+ if (boldStyle == null)
+ {
+ boldStyle = new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold };
+ }
+
+ if (selectionGridStyle == null)
+ {
+ selectionGridStyle = new GUIStyle(GUI.skin.button) { fixedHeight = 48, alignment = TextAnchor.MiddleLeft, padding = new RectOffset(4, 4, 4, 4) };
+ }
+
+ if (feedbackButtonStyle == null)
+ {
+ feedbackButtonStyle = new GUIStyle(GUI.skin.button) { fixedHeight = 32, fixedWidth = 32, imagePosition = ImagePosition.ImageLeft };
+ }
+ }
+
+ private static long ConvertUnityVersionToNumber(string version)
+ {
+ var matches = Regex.Matches(version, @"(\d+)\.(\d+)\.(\d+).*");
+
+ if (matches.Count < 1)
+ {
+ throw new InvalidOperationException();
+ }
+
+ var groups = matches[0].Groups;
+
+ // groups[0] is the whole string that matches; the following elements are the captured matches.
+ return long.Parse(groups[1].Value) * 100 + long.Parse(groups[2].Value) * 10 + long.Parse(groups[3].Value);
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWindow.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWindow.cs.meta
new file mode 100644
index 0000000..d269784
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWindow.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 96748f9e4de7719419aea56075a8a14a
+timeCreated: 1515171104
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWorker.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWorker.cs
new file mode 100644
index 0000000..f666b49
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWorker.cs
@@ -0,0 +1,62 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+
+namespace Improbable.Unity.Editor.Core
+{
+ ///
+ /// Represents a worker in the current project.
+ ///
+ public class SpatialOsWorker : IEquatable
+ {
+ ///
+ /// The name of the worker.
+ ///
+ public string Name { get; private set; }
+
+ internal SpatialOsWorker(string name)
+ {
+ Name = name;
+ }
+
+ public bool Equals(SpatialOsWorker other)
+ {
+ if (ReferenceEquals(null, other))
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ return string.Equals(Name, other.Name);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj))
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+
+ if (obj.GetType() != GetType())
+ {
+ return false;
+ }
+
+ return Equals((SpatialOsWorker) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return (Name != null ? Name.GetHashCode() : 0);
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWorker.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWorker.cs.meta
new file mode 100644
index 0000000..7ae4856
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialOsWorker.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 15592119097408b4787af410c744110c
+timeCreated: 1484056898
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialRunner.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialRunner.cs
new file mode 100644
index 0000000..3ee72ef
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialRunner.cs
@@ -0,0 +1,120 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.Editor
+{
+ ///
+ /// Helper to allow for running the spatial CLI tool.
+ ///
+ static class SpatialRunner
+ {
+ // Ensure that a new script file is written during a particular session.
+ private static int processRunCounter;
+
+ private const string SpatialCommandLaunchFailure =
+ "Could not launch spatial. Make sure it was on the PATH when Unity launched, " +
+ "the Unity process has permissions to execute it, " +
+ "and the binary is not corrupted (e.g. by running spatial update).\n" +
+ "Error Code 0x{0:X8}\n{1}\n";
+
+ public const string DefaultSpatialCommand = "spatial";
+
+ public const string CommandLocationKey = "Improbable.Unity.Editor.Addons.SpatialCommand.Location";
+
+ ///
+ /// Temporary solution for running external processes.
+ ///
+ public static void RunPausedProcess(string command, string arguments, string workingDir)
+ {
+ string scriptPath = Path.Combine(Path.GetTempPath(), string.Format("unitysdk_spatialos_command_{0}", processRunCounter++));
+ string scriptCommand;
+ string scriptArguments;
+
+ if (Application.platform == RuntimePlatform.WindowsEditor)
+ {
+ scriptCommand = "cmd";
+ scriptPath += ".cmd";
+ scriptArguments = string.Format("/c \"{0}\"", scriptPath);
+
+ File.WriteAllText(scriptPath, string.Format("@{0} {1}\r\n@pause\r\n@del \"{2}\"", command, arguments, scriptPath));
+ }
+ else
+ {
+ scriptCommand = "open";
+ scriptPath += ".command";
+ scriptArguments = string.Format("\"{0}\"", scriptPath);
+ File.WriteAllText(scriptPath, string.Format("cd \"{0}\"\n{1} {2}\nread -n 1 -s -p \"Press any key to continue\"\nrm \"{3}\"", workingDir, command, arguments, scriptPath));
+
+ MakeScriptAsExecutable(scriptPath);
+ }
+
+ var startInfo = new ProcessStartInfo(scriptCommand, scriptArguments)
+ {
+ WorkingDirectory = workingDir,
+ UseShellExecute = false,
+ CreateNoWindow = false,
+ WindowStyle = ProcessWindowStyle.Normal
+ };
+
+ // Try to use what the user has setup before.
+ var fullPathToSpatial = EditorPrefs.GetString(CommandLocationKey, DefaultSpatialCommand);
+
+ var process = RunCommandWithSpatialInThePath(fullPathToSpatial, startInfo);
+ if (process != null)
+ {
+ process.Close();
+ }
+ }
+
+ private static void MakeScriptAsExecutable(string scriptPath)
+ {
+ var chmodStartInfo = new ProcessStartInfo("chmod", string.Format("+x \"{0}\"", scriptPath))
+ {
+ UseShellExecute = false,
+ CreateNoWindow = true
+ };
+
+ using (var chmodProcess = Process.Start(chmodStartInfo))
+ {
+ if (chmodProcess != null)
+ {
+ chmodProcess.WaitForExit();
+ }
+ }
+ }
+
+ public static Process RunCommandWithSpatialInThePath(string fullPathToSpatial, ProcessStartInfo startInfo)
+ {
+ var spatialLocation = Path.GetDirectoryName(fullPathToSpatial);
+
+ if (!string.IsNullOrEmpty(spatialLocation))
+ {
+ var newPathEnv = Environment.GetEnvironmentVariable("PATH");
+ newPathEnv = string.Format("{0}{1}{2}", newPathEnv, Path.PathSeparator, spatialLocation);
+ startInfo.EnvironmentVariables["PATH"] = newPathEnv;
+ }
+
+ // Ensure UNITY_HOME reflects the running instance of Unity.
+ // EditorApplication.applicationContentsPath returns "/Editor/Data", so strip the upper two directories.
+ var combined = Path.Combine(EditorApplication.applicationContentsPath, "..");
+ var applicationPath = Path.GetFullPath(Path.Combine(combined, ".."));
+ startInfo.EnvironmentVariables["UNITY_HOME"] = applicationPath;
+
+ try
+ {
+ return Process.Start(startInfo);
+ }
+ catch (Win32Exception e)
+ {
+ var errorMessage = string.Format(SpatialCommandLaunchFailure, e.ErrorCode, e.Message);
+ throw new Exception(errorMessage);
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialRunner.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialRunner.cs.meta
new file mode 100644
index 0000000..4bcbd8a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SpatialRunner.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 19fc60b5719e31c44a6412ff8ad5d8f4
+timeCreated: 1515410418
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SupportPopup.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SupportPopup.cs
new file mode 100644
index 0000000..26ce46a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SupportPopup.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.Diagnostics;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools
+{
+ internal class SupportPopup : PopupWindowContent
+ {
+ public override Vector2 GetWindowSize()
+ {
+ return new Vector2(120, 68);
+ }
+
+ public override void OnGUI(Rect rect)
+ {
+ using (new EditorGUILayout.VerticalScope())
+ {
+ if (GUILayout.Button("Forums"))
+ {
+ Process.Start("https://forums.improbable.io");
+ }
+
+ if (GUILayout.Button("Documentation"))
+ {
+ Process.Start("https://spatialos.improbable.io/docs");
+ }
+
+ if (GUILayout.Button("Send feedback"))
+ {
+ Process.Start("https://forums.improbable.io/c/fb");
+ }
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SupportPopup.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SupportPopup.cs.meta
new file mode 100644
index 0000000..e4b81df
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/SupportPopup.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 9896d06ab57edb1449e66dd4997c68cb
+timeCreated: 1494404803
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/WorkerSelection.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/WorkerSelection.cs
new file mode 100644
index 0000000..52e2c31
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/WorkerSelection.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+
+namespace Improbable.Unity.Editor.Core
+{
+ ///
+ /// Manages the selection of the available workers in the Unity project.
+ ///
+ public class WorkerSelection
+ {
+ private readonly HashSet selectedWorkers = new HashSet();
+
+ ///
+ /// Invoked when the set of selected workers changes.
+ ///
+ public event Action> OnWorkerSelectionChanged;
+
+ ///
+ /// Selects or deselects a worker.
+ ///
+ public void SelectWorker(SpatialOsWorker worker, bool selectWorker)
+ {
+ var changed = selectWorker ? selectedWorkers.Add(worker) : selectedWorkers.Remove(worker);
+
+ if (changed && OnWorkerSelectionChanged != null)
+ {
+ OnWorkerSelectionChanged(selectedWorkers);
+ }
+ }
+
+ ///
+ /// A set of selected worker instances.
+ ///
+ public HashSet SelectedWorkers
+ {
+ get { return new HashSet(selectedWorkers); }
+ }
+
+ ///
+ /// Returns true if any workers are selected.
+ ///
+ public bool AnyWorkersSelected
+ {
+ get { return selectedWorkers.Count > 0; }
+ }
+
+ ///
+ /// Returns true if the worker is selected.
+ ///
+ public bool IsWorkerSelected(SpatialOsWorker worker)
+ {
+ return selectedWorkers.Contains(worker);
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/WorkerSelection.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/WorkerSelection.cs.meta
new file mode 100644
index 0000000..8654ca0
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Core/WorkerSelection.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: c76827b39d38fbe4a82de46375bec7ed
+timeCreated: 1484231158
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor.meta
new file mode 100644
index 0000000..f833a64
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 9556988323f446d4bb85777e81b94c09
+folderAsset: yes
+timeCreated: 1471955846
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/.iOSBuildTools/XcodeSettingsBuilder.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/.iOSBuildTools/XcodeSettingsBuilder.cs
new file mode 100644
index 0000000..54727ac
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/.iOSBuildTools/XcodeSettingsBuilder.cs
@@ -0,0 +1,103 @@
+
+using System.IO;
+using UnityEditor;
+using UnityEditor.iOS.Xcode;
+using UnityEngine;
+
+namespace Improbable.Editor.Addons.iOSBuildTools
+{
+ ///
+ /// Configures and updates all Xcode build settings to be compatible with
+ /// SpatialOS and iOS Player build targets.
+ ///
+
+ [InitializeOnLoad]
+ internal static class XcodeSettingsBuilder
+ {
+ private const string WORKER_BUILD_FOLDER = "build/worker";
+ private const string WORKER_NAME = "UnityClient@iOS";
+ private const string CORE_SDK_DLL_NAME = "libCoreSdkDll.dylib";
+
+ [MenuItem("Improbable/iOS/Update Xcode Build Settings (Device)")]
+ public static void UpdateXcodeBuildSettingsForDevice()
+ {
+ UpdateXcodeBuildSettings(GetCoreSdkDllFolderForDevice());
+ }
+
+ [MenuItem("Improbable/iOS/Update Xcode Build Settings (Simulator)")]
+ public static void UpdateXcodeBuildSettingsForSimulator()
+ {
+ UpdateXcodeBuildSettings(GetCoreSdkDllFolderForSimulator());
+ }
+
+ public static void UpdateXcodeBuildSettings(string dylibFolderPath)
+ {
+ var unityWorkerRoot = Path.Combine(Application.dataPath, "..");
+ var iOSPlayerBuildFolder = Path.Combine(WORKER_BUILD_FOLDER, Path.Combine(WORKER_NAME, WORKER_NAME));
+ var defaultBuildFolderPath = Path.Combine(unityWorkerRoot, iOSPlayerBuildFolder);
+
+ UpdateXcodeBuildSettingsAt(defaultBuildFolderPath, dylibFolderPath);
+ }
+
+ private static void UpdateXcodeBuildSettingsAt(string buildFolderPath, string dylibFolderPath)
+ {
+ // Get path to build/workers/UnityClient@iOS/UnityClient@iOS/Unity-iPhone.xcodeproj
+ string projectPath = PBXProject.GetPBXProjectPath(buildFolderPath);
+
+ if (!File.Exists(projectPath))
+ {
+ Debug.LogErrorFormat("Unable to find pbxproj file at: {0}", projectPath);
+ return;
+ }
+
+ // Load the current Xcode project
+ PBXProject project = new PBXProject();
+ project.ReadFromFile(projectPath);
+
+ // Get Xcode Target GUID of "Unity-iPhone"
+ var targetGuid = project.TargetGuidByName("Unity-iPhone");
+
+ // Copy Core SDK .dylib from Assets/Plugins to Xcode project
+ var dylibSource = Path.Combine(dylibFolderPath, CORE_SDK_DLL_NAME);
+ var dylibTarget = Path.Combine(buildFolderPath, Path.Combine("Data", CORE_SDK_DLL_NAME));
+ File.Copy(dylibSource, dylibTarget, true /* overwriteExisting */);
+
+ // Add the .dylib to the Xcode project
+ var dylibGuid = project.AddFile(dylibTarget, Path.Combine("Data", CORE_SDK_DLL_NAME), PBXSourceTree.Source);
+ project.AddFileToBuild(targetGuid, dylibGuid);
+
+ // Disable bitcode
+ project.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO");
+
+ // Add @executable_path to runtime search paths so that @rpath/libCoreSdkDll.dylib resolves to
+ // a path that can be located by Xcode and iOS devices.
+ project.SetBuildProperty(targetGuid, "LD_RUNPATH_SEARCH_PATHS", "@executable_path");
+ project.AddBuildProperty(targetGuid, "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Data");
+
+ // Fix library search paths generated by Unity by ensuring that quotation marks are not placed
+ // around the search paths.
+ project.SetBuildProperty(targetGuid, "LIBRARY_SEARCH_PATHS", "$(inherited)");
+ project.AddBuildProperty(targetGuid, "LIBRARY_SEARCH_PATHS", "$(SRCROOT)");
+ project.AddBuildProperty(targetGuid, "LIBRARY_SEARCH_PATHS", "$(SRCROOT)/Libraries");
+
+ // Add the Data folder as a library search path to locate the libCoreSdkDll.dylib at link time.
+ project.AddBuildProperty(targetGuid, "LIBRARY_SEARCH_PATHS", "$(SRCROOT)/Data");
+
+ // Write all changes to the Xcode project file.
+ project.WriteToFile(projectPath);
+
+ // Output a useful message.
+ Debug.LogFormat("Successfully updated Xcode project settings for: {0}", projectPath);
+ }
+
+ private static string GetCoreSdkDllFolderForSimulator()
+ {
+ return Path.Combine(Application.dataPath, "Plugins/Improbable/darwin_ios-x86_64_dll");
+ }
+
+ private static string GetCoreSdkDllFolderForDevice()
+ {
+ return Path.Combine(Application.dataPath, "Plugins/Improbable/darwin_ios-arm_dll");
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/.iOSBuildTools/XcodeSettingsBuilder.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/.iOSBuildTools/XcodeSettingsBuilder.cs.meta
new file mode 100644
index 0000000..a079d0e
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/.iOSBuildTools/XcodeSettingsBuilder.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 38db9d234f6d64c79b612c8e4eb6d834
+timeCreated: 1485438136
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialBuild.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialBuild.meta
new file mode 100644
index 0000000..d03cf95
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialBuild.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 3bff1f3f1c725cd4ca7bb0a8c4003baf
+folderAsset: yes
+timeCreated: 1484060314
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialBuild/SpatialBuild.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialBuild/SpatialBuild.cs
new file mode 100644
index 0000000..d693bb4
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialBuild/SpatialBuild.cs
@@ -0,0 +1,372 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Improbable.Unity.Editor.Addons;
+using Improbable.Unity.Editor.Core;
+using Improbable.Unity.EditorTools.Build;
+using Improbable.Unity.EditorTools.PrefabExport;
+using Improbable.Unity.EditorTools.Util;
+using Improbable.Unity.Util;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Editor.Addons.SpatialBuild
+{
+ [InitializeOnLoad]
+ public class SpatialBuild : ISpatialOsEditorAddon, ISpatialOsEditorAddonSettings, ISpatialOsEditorAddonBuild
+ {
+ private readonly string[] configurations = { DevelopmentTarget, DeploymentTarget };
+
+ private int selectedConfiguration;
+
+ private IList buildTargets;
+ private string selectedWorkerString;
+
+ internal const string DeploymentTarget = "Deployment";
+ internal const string DevelopmentTarget = "Development";
+
+ private readonly string selectedWorkerTargetPath = PathUtil.Combine(SpatialOsEditor.WorkerRootDir, "build", "player_build_target.txt");
+
+ ///
+ public string Name
+ {
+ get { return "Build"; }
+ }
+
+ ///
+ public string Vendor
+ {
+ get { return "Improbable Worlds, Ltd."; }
+ }
+
+ private string SelectedConfiguration
+ {
+ get { return configurations[selectedConfiguration]; }
+ }
+
+ internal SpatialBuild()
+ {
+ try
+ {
+ var sourceText = File.ReadAllText(selectedWorkerTargetPath);
+ selectedConfiguration = System.Math.Max(0, System.Math.Min(int.Parse(sourceText), configurations.Length - 1));
+ }
+ catch { }
+
+ SpatialOsEditor.WorkerSelection.OnWorkerSelectionChanged += WorkerSelectionChanged;
+ }
+
+ private void WorkerSelectionChanged(HashSet spatialOsWorkers)
+ {
+ buildTargets = null;
+ }
+
+ private void EditorUpdate()
+ {
+ EditorApplication.update -= EditorUpdate;
+ BuildWorkers();
+ }
+
+ ///
+ public void OnDevGui(Rect rect)
+ {
+ using (new EditorGUILayout.VerticalScope())
+ {
+ DrawWorkerTargetSelection();
+
+ using (new EditorGUI.DisabledScope(!SpatialOsEditor.WorkerSelection.AnyWorkersSelected || buildTargets == null || buildTargets.Count == 0))
+ {
+ using (new EditorGUILayout.VerticalScope())
+ {
+ GUILayout.Label("Generate from schema", EditorStyles.boldLabel);
+
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ if (GUILayout.Button(new GUIContent("Build", "Generate code for the currently selected workers."), EditorStyles.toolbarButton))
+ {
+ CodegenWorkers();
+ }
+
+ if (GUILayout.Button(new GUIContent("Cleanup", "Generate code for the currently selected workers."), EditorStyles.toolbarButton))
+ {
+ CleanupCodegen();
+ }
+ }
+ }
+
+ EditorGUILayout.Separator();
+
+ using (new EditorGUILayout.VerticalScope())
+ {
+ GUILayout.Label("Entity prefabs", EditorStyles.boldLabel);
+
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ if (GUILayout.Button(new GUIContent("Build all", "Build entity prefabs for the selected workers and targets."), EditorStyles.toolbarButton))
+ {
+ ExportPrefabs();
+ }
+
+ using (new EditorGUI.DisabledScope(!EntityPrefabExporter.AnyPrefabsSelected()))
+ {
+ if (GUILayout.Button(new GUIContent("Build selected", "Build selected entity prefabs for the selected workers and targets."), EditorStyles.toolbarButton))
+ {
+ ExportSelectedPrefabs();
+ }
+ }
+
+ if (GUILayout.Button("Cleanup all", EditorStyles.toolbarButton))
+ {
+ EntityPrefabExportMenus.OnCleanEntityPrefabs();
+ }
+ }
+ }
+
+ EditorGUILayout.Separator();
+
+ using (new EditorGUILayout.VerticalScope())
+ {
+ GUILayout.Label("Workers", EditorStyles.boldLabel);
+
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ if (GUILayout.Button(new GUIContent("Build", "Build the currently selected workers."), EditorStyles.toolbarButton))
+ {
+ EditorApplication.update += EditorUpdate;
+ }
+
+ if (GUILayout.Button(new GUIContent("Cleanup all", "Clean all workers."), EditorStyles.toolbarButton))
+ {
+ UnityPathUtil.EnsureDirectoryClean(UnityPlayerBuilder.PlayerBuildDirectory);
+ UnityPathUtil.EnsureDirectoryClean(UnityPlayerBuilder.PlayerBuildScratchDirectory);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void OnSettingsGui(Rect rect)
+ {
+ var patchingEnabled = UnityPlayerBuilderMenu.IsAutopatchEnabled();
+ if (patchingEnabled != EditorGUILayout.ToggleLeft("Autopatch workers", patchingEnabled))
+ {
+ UnityPlayerBuilderMenu.ToggleAutopatch();
+ }
+ }
+
+ private void BuildWorkers()
+ {
+ var workersToBuild = GetSelectedWorkerNames();
+ var sw = new System.Diagnostics.Stopwatch();
+ sw.Start();
+
+ try
+ {
+ switch (SelectedConfiguration)
+ {
+ case DevelopmentTarget:
+ UnityPlayerBuilders.BuildDevelopmentPlayers(workersToBuild);
+ break;
+ case DeploymentTarget:
+ UnityPlayerBuilders.BuildDeploymentPlayers(workersToBuild);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(string.Format("{0} is an unknown build target.", SelectedConfiguration));
+ }
+ }
+ finally
+ {
+ sw.Stop();
+ }
+ }
+
+ private static void CodegenWorkers()
+ {
+ var selectedWorkers = string.Join(" ", SpatialOsEditor.WorkerSelection.SelectedWorkers.Select(w => w.Name).ToArray());
+ SpatialOsEditor.RunPausedProcess(SpatialCommand.SpatialPath, "codegen " + selectedWorkers, SpatialOsEditor.WorkerRootDir);
+ }
+
+ private static void CleanupCodegen()
+ {
+ var codegenOutputDir = PathUtil.Combine(Directory.GetCurrentDirectory(), EditorPaths.CodeGeneratorScratchDirectory).ToUnityPath();
+ if (!Directory.Exists(codegenOutputDir))
+ {
+ Debug.LogFormat("Code generator output directory '{0}' does not exist. Nothing to clean.", codegenOutputDir);
+ return;
+ }
+
+ try
+ {
+ Debug.LogFormat("Removing code generator output from '{0}'.", codegenOutputDir);
+ Directory.Delete(codegenOutputDir, true);
+ }
+ catch (Exception e)
+ {
+ Debug.LogErrorFormat("Exception was thrown when cleaning codegen output.");
+ Debug.LogException(e);
+ }
+ }
+
+ private void ExportPrefabs()
+ {
+ var targets = GetWorkerBuilders();
+ foreach (var target in targets)
+ {
+ EntityPrefabExporter.ExportAllEntityPrefabs(target.BuildTarget);
+ }
+ }
+
+ private void ExportSelectedPrefabs()
+ {
+ var targets = GetWorkerBuilders();
+ foreach (var target in targets)
+ {
+ EntityPrefabExporter.ExportSelectedEntityPrefabs(target.BuildTarget);
+ }
+ }
+
+ private void DrawWorkerTargetSelection()
+ {
+ using (new EditorGUILayout.VerticalScope())
+ {
+ using (var check = new EditorGUI.ChangeCheckScope())
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ selectedConfiguration = EditorGUILayout.Popup(selectedConfiguration, configurations);
+ if (check.changed)
+ {
+ buildTargets = null;
+ try
+ {
+ File.WriteAllText(selectedWorkerTargetPath, selectedConfiguration.ToString());
+ }
+ catch { }
+ }
+ }
+
+ using (new EditorGUILayout.VerticalScope())
+ {
+ var workers = SpatialOsEditor.Workers;
+ if (workers.Count == 0)
+ {
+ GUILayout.Label("No workers found.");
+ return;
+ }
+
+ for (var i = 0; i < workers.Count; i++)
+ {
+ var worker = workers[i];
+
+ using (var check = new EditorGUI.ChangeCheckScope())
+ {
+ var selected = EditorGUILayout.ToggleLeft(worker.Name, SpatialOsEditor.WorkerSelection.IsWorkerSelected(worker));
+
+ if (check.changed)
+ {
+ SpatialOsEditor.WorkerSelection.SelectWorker(worker, selected);
+ }
+ }
+ }
+ }
+ }
+
+ CalculateBuildTargets();
+
+ using (new EditorGUILayout.VerticalScope())
+ {
+ EditorGUILayout.Separator();
+
+ GUILayout.Label("Output platforms", EditorStyles.boldLabel);
+
+ if (buildTargets == null || buildTargets.Count == 0)
+ {
+ if (string.IsNullOrEmpty(selectedWorkerString))
+ {
+ selectedWorkerString = string.Join(" and ", SpatialOsEditor.WorkerSelection.SelectedWorkers.Select(w => w.Name).ToArray());
+ }
+
+ EditorGUILayout.HelpBox(string.Format("The current selection of \"{0}\", filtered by the worker types \"{1}\", will not output any workers or prefabs.", SelectedConfiguration, selectedWorkerString), MessageType.Warning);
+ }
+ else
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ EditorGUILayout.Space();
+
+ using (new EditorGUILayout.VerticalScope())
+ {
+ for (var i = 0; i < buildTargets.Count; i++)
+ {
+ {
+ GUILayout.Label(buildTargets[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private IList GetSelectedWorkerNames()
+ {
+ return SpatialOsEditor.WorkerSelection.SelectedWorkers.Select(w => w.Name).ToList();
+ }
+
+ private IList GetWorkerBuilders()
+ {
+ var workersToBuild = GetSelectedWorkerNames();
+
+ switch (SelectedConfiguration)
+ {
+ case DevelopmentTarget:
+ return UnityPlayerBuilders.DevelopmentPlayerBuilders(workersToBuild);
+ case DeploymentTarget:
+ return UnityPlayerBuilders.DeploymentPlayerBuilders(workersToBuild);
+ default:
+ throw new ArgumentOutOfRangeException(string.Format("{0} is an unknown build target.", SelectedConfiguration));
+ }
+ }
+
+ static SpatialBuild()
+ {
+ SpatialOsEditor.RegisterAddon(new SpatialBuild());
+ }
+
+ ///
+ /// Calculate and cache the set of based on the target type (Development|Deployment) and
+ /// selected workers.
+ ///
+ private void CalculateBuildTargets()
+ {
+ if (buildTargets == null)
+ {
+ if (!SpatialOsEditor.WorkerSelection.AnyWorkersSelected)
+ {
+ buildTargets = new List();
+ }
+ else
+ {
+ var workersToBuild = GetSelectedWorkerNames();
+ IList builders;
+ switch (SelectedConfiguration)
+ {
+ case DevelopmentTarget:
+ builders = UnityPlayerBuilders.DevelopmentPlayerBuilders(workersToBuild);
+ break;
+ case DeploymentTarget:
+ builders = UnityPlayerBuilders.DeploymentPlayerBuilders(workersToBuild);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(string.Format("{0} is an unknown build target.", SelectedConfiguration));
+ }
+
+ buildTargets = builders.Select(b => b.BuildTarget.ToString()).Distinct().OrderBy(b => b).ToList();
+ }
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialBuild/SpatialBuild.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialBuild/SpatialBuild.cs.meta
new file mode 100644
index 0000000..0d4c5e5
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialBuild/SpatialBuild.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ce0a67b8a70d3de49bd5c9f8a0faea84
+timeCreated: 1484060314
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialConnect.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialConnect.meta
new file mode 100644
index 0000000..21ac98d
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialConnect.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: ae688f67d38883f46b380004ff8cbf31
+folderAsset: yes
+timeCreated: 1481561131
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialConnect/SpatialConnect.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialConnect/SpatialConnect.cs
new file mode 100644
index 0000000..8e60d99
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialConnect/SpatialConnect.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using Improbable.Unity.Editor.Addons;
+using Improbable.Unity.Editor.Core;
+using Improbable.Unity.EditorTools;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Editor.Addons.SpatialConnect
+{
+ [InitializeOnLoad]
+ public class SpatialConnect : ISpatialOsEditorAddon, ISpatialOsEditorAddonBuild
+ {
+ private string deploymentName;
+
+ ///
+ public string Name
+ {
+ get { return "Connect to deployment"; }
+ }
+
+ ///
+ public string Vendor
+ {
+ get { return "Improbable Worlds, Ltd."; }
+ }
+
+ static SpatialConnect()
+ {
+ SpatialOsEditor.RegisterAddon(new SpatialConnect());
+ }
+
+ ///
+ public void OnDevGui(Rect rect)
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUILayout.Label("Deployment name", SpatialOsWindow.SharedContent.MinimalLabelStyle);
+ deploymentName = EditorGUILayout.TextField(deploymentName);
+
+ using (new EditorGUI.DisabledScope(string.IsNullOrEmpty(deploymentName)))
+ {
+ if (GUILayout.Button("Connect"))
+ {
+ SpatialOsEditor.RunPausedProcess(SpatialCommand.SpatialPath, string.Format("connect {0} {1}", SpatialOsEditor.ProjectDescriptor.Name, deploymentName), SpatialOsEditor.ApplicationRootDir);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialConnect/SpatialConnect.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialConnect/SpatialConnect.cs.meta
new file mode 100644
index 0000000..ddd8eba
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialConnect/SpatialConnect.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 9477feca0c8392d4cb6e1ccc650c8abf
+timeCreated: 1481561131
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialLocal.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialLocal.meta
new file mode 100644
index 0000000..c241d07
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialLocal.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 0c36411904fd30d47922675f7f5b9b43
+folderAsset: yes
+timeCreated: 1481560970
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialLocal/SpatialLocal.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialLocal/SpatialLocal.cs
new file mode 100644
index 0000000..bf55625
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialLocal/SpatialLocal.cs
@@ -0,0 +1,54 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.IO;
+using System.Linq;
+using Improbable.Unity.Editor.Addons;
+using Improbable.Unity.Editor.Core;
+using Improbable.Unity.Util;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Editor.Addons.SpatialLocal
+{
+ [InitializeOnLoad]
+ public class SpatialLocal : ISpatialOsEditorAddon, ISpatialOsEditorAddonBuild
+ {
+ public string Name
+ {
+ get { return "Run SpatialOS locally"; }
+ }
+
+ public string Vendor
+ {
+ get { return "Improbable Worlds, Ltd."; }
+ }
+
+ private int selectedConfig;
+
+ static SpatialLocal()
+ {
+ SpatialOsEditor.RegisterAddon(new SpatialLocal());
+ }
+
+ public void OnDevGui(Rect rect)
+ {
+ using (new EditorGUILayout.VerticalScope())
+ {
+ var rootPath = Path.GetFullPath(PathUtil.Combine(SpatialOsEditor.WorkerRootDir, "..", ".."));
+ var files = Directory.GetFiles(rootPath, "*.json");
+ var filtered = files.Where(f => !f.Contains("spatialos")).Select(Path.GetFileName).ToArray();
+ selectedConfig = GUILayout.SelectionGrid(selectedConfig, filtered, 1);
+
+ using (new EditorGUI.DisabledScope(selectedConfig < 0 || selectedConfig >= filtered.Length))
+ {
+ EditorGUILayout.Space();
+
+ if (GUILayout.Button("Run"))
+ {
+ SpatialOsEditor.RunPausedProcess(SpatialCommand.SpatialPath, string.Format("local start {0}", filtered[selectedConfig]), rootPath);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialLocal/SpatialLocal.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialLocal/SpatialLocal.cs.meta
new file mode 100644
index 0000000..45aa0e8
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialLocal/SpatialLocal.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 9c2ce9381a58c9a448091a1bf237968c
+timeCreated: 1481561131
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialUpload.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialUpload.meta
new file mode 100644
index 0000000..cd3ff2e
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialUpload.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 1b2f275bec3ac674d9596dcc0636ea67
+folderAsset: yes
+timeCreated: 1481621743
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialUpload/SpatialUpload.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialUpload/SpatialUpload.cs
new file mode 100644
index 0000000..8fb5ecd
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialUpload/SpatialUpload.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using Improbable.Unity.Editor.Addons;
+using Improbable.Unity.Editor.Core;
+using Improbable.Unity.EditorTools;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Editor.Addons.SpatialUpload
+{
+ [InitializeOnLoad]
+ public class SpatialUpload : ISpatialOsEditorAddon, ISpatialOsEditorAddonBuild
+ {
+ private string assemblyName;
+
+ ///
+ public string Name
+ {
+ get { return "Spatial upload"; }
+ }
+
+ ///
+ public string Vendor
+ {
+ get { return "Improbable Worlds, Ltd."; }
+ }
+
+ ///
+ public void OnDevGui(Rect rect)
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUILayout.Label("Assembly name", SpatialOsWindow.SharedContent.MinimalLabelStyle);
+ assemblyName = EditorGUILayout.TextField(assemblyName);
+
+ if (GUILayout.Button("Upload"))
+ {
+ SpatialOsEditor.RunPausedProcess(SpatialCommand.SpatialPath, "upload " + assemblyName, SpatialOsEditor.ApplicationRootDir);
+ }
+ }
+ }
+
+ static SpatialUpload()
+ {
+ SpatialOsEditor.RegisterAddon(new SpatialUpload());
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialUpload/SpatialUpload.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialUpload/SpatialUpload.cs.meta
new file mode 100644
index 0000000..e62c4ed
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Editor/SpatialUpload/SpatialUpload.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 4b455904a16647847912067b817b9a24
+timeCreated: 1481621744
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Init.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Init.meta
new file mode 100644
index 0000000..63d8e1d
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Init.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 79b7354d6621ea544be9f48865593bbe
+folderAsset: yes
+timeCreated: 1444833351
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Init/InitializeSDKLogging.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Init/InitializeSDKLogging.cs
new file mode 100644
index 0000000..07089cc
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Init/InitializeSDKLogging.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using Improbable.Worker;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Init
+{
+ [InitializeOnLoad]
+ class InitializeSDKLogging
+ {
+ static InitializeSDKLogging()
+ {
+ ClientError.ExceptionCallback = Debug.LogException;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Init/InitializeSDKLogging.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Init/InitializeSDKLogging.cs.meta
new file mode 100644
index 0000000..83faa7a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Init/InitializeSDKLogging.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 98433a43b744a6c439323bb46b6ae27d
+timeCreated: 1496916327
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Internal.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Internal.meta
new file mode 100644
index 0000000..c0d837b
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Internal.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 02f905050e9f2a14a8ec074d702369e3
+folderAsset: yes
+timeCreated: 1515492928
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Internal/EditorObjectStateManager.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Internal/EditorObjectStateManager.cs
new file mode 100644
index 0000000..ed66264
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Internal/EditorObjectStateManager.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.Collections.Generic;
+
+namespace Improbable.Unity.CodeGeneration
+{
+ ///
+ /// Utility class for storing state of Unity editor components.
+ ///
+ public static class EditorObjectStateManager
+ {
+ private static readonly IDictionary EditorDataObjects =
+ new Dictionary();
+
+ public static IComponentEditorDataObject GetComponentEditorData(int objectHash)
+ {
+ IComponentEditorDataObject value;
+ return EditorDataObjects.TryGetValue(objectHash, out value) ? value : null;
+ }
+
+ public static void SetComponentEditorData(int objectHash, IComponentEditorDataObject editorData)
+ {
+ EditorDataObjects[objectHash] = editorData;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Internal/EditorObjectStateManager.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Internal/EditorObjectStateManager.cs.meta
new file mode 100644
index 0000000..b2ca20c
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Internal/EditorObjectStateManager.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: e4b5478947f037c41a6aba7c35e686bc
+timeCreated: 1490268491
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport.meta
new file mode 100644
index 0000000..b25b0ab
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: f19c68b39cdc139438cb255c9f485018
+folderAsset: yes
+timeCreated: 1444833352
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabDirectoryCleaner.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabDirectoryCleaner.cs
new file mode 100644
index 0000000..98e015b
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabDirectoryCleaner.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.IO;
+using Improbable.Unity.EditorTools.Util;
+
+namespace Improbable.Unity.EditorTools.PrefabExport
+{
+ static class EntityPrefabDirectoryCleaner
+ {
+ public static void CleanPrefabTargetDirectories()
+ {
+ var info = new DirectoryInfo(EditorPaths.PrefabExportDirectory);
+ if (info.Exists)
+ {
+ var files = info.GetFiles();
+ foreach (var fileInfo in files)
+ {
+ fileInfo.Delete();
+ }
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabDirectoryCleaner.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabDirectoryCleaner.cs.meta
new file mode 100644
index 0000000..06134c9
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabDirectoryCleaner.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: e3f760e3de3c4f24faeb90d5ae22fbb9
+timeCreated: 1444833362
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExportMenus.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExportMenus.cs
new file mode 100644
index 0000000..bf7b9c8
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExportMenus.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using UnityEditor;
+
+namespace Improbable.Unity.EditorTools.PrefabExport
+{
+ public class EntityPrefabExportMenus
+ {
+ ///
+ /// This is called whenever entity prefabs need to be exported for all build targets.
+ /// This can be done from within the editor, or from external sources like build systems.
+ /// By default its value is the baseline behaviour, which can be saved off and invoked
+ /// as part of a custom chain of events.
+ ///
+ public static Action OnExportAllEntityPrefabs = EntityPrefabExporter.ExportAllEntityPrefabs;
+
+ ///
+ /// This is called whenever entity prefabs need to be exported for development targets.
+ /// This can be done from within the editor, or from external sources like build systems.
+ /// By default its value is the baseline behaviour, which can be saved off and invoked
+ /// as part of a custom chain of events.
+ ///
+ public static Action OnExportDevelopmentEntityPrefabs = EntityPrefabExporter.ExportDevelopmentEntityPrefabs;
+
+ ///
+ /// This is called when we need to export the entity prefabs that are currently selected in the Project view.
+ ///
+ public static Action OnExportSelectedEntityPrefabs = EntityPrefabExporter.ExportSelectedEntityPrefabs;
+
+ ///
+ /// This is called when we need to export the entity prefabs that are currently selected in the Project view.
+ ///
+ public static Action OnExportSelectedDevelopmentEntityPrefabs = EntityPrefabExporter.ExportSelectedDevelopmentEntityPrefabs;
+
+ ///
+ /// This is called whenever entity prefabs need to be cleaned.
+ /// This can be done from within the editor, or from external sources like build systems.
+ /// By default its value is the baseline behaviour, which can be saved off and invoked
+ /// as part of a custom chain of events.
+ ///
+ public static Action OnCleanEntityPrefabs = EntityPrefabDirectoryCleaner.CleanPrefabTargetDirectories;
+
+ [MenuItem("Improbable/Prefabs/Clean entity prefabs")]
+ public static void CleanAllEntityPrefabs()
+ {
+ OnCleanEntityPrefabs();
+ }
+
+ [MenuItem("Improbable/Prefabs/Export all entity prefabs %E")]
+ [MenuItem("Assets/Export all entity prefabs %E")]
+ public static void ExportAllEntityPrefabs()
+ {
+ OnExportAllEntityPrefabs();
+ }
+
+ [MenuItem("Improbable/Prefabs/Export development entity prefabs")]
+ [MenuItem("Assets/Export development entity prefabs")]
+ public static void ExportDevelopmentEntityPrefabs()
+ {
+ OnExportDevelopmentEntityPrefabs();
+ }
+
+ [MenuItem("Improbable/Prefabs/Export selected entity prefabs %S")]
+ [MenuItem("Assets/Export selected entity prefabs %S")]
+ public static void ExportSelectedEntityPrefabs()
+ {
+ OnExportSelectedEntityPrefabs();
+ }
+
+ [MenuItem("Improbable/Prefabs/Export selected development entity prefabs")]
+ [MenuItem("Assets/Export selected development entity prefabs")]
+ public static void ExportSelectedDevelopmentEntityPrefabs()
+ {
+ OnExportSelectedDevelopmentEntityPrefabs();
+ }
+
+ [MenuItem("Improbable/Prefabs/Export selected entity prefabs %S", true)]
+ [MenuItem("Assets/Export selected entity prefabs %S", true)]
+ private static bool ExportSelectedEntityPrefabsValidation()
+ {
+ return EntityPrefabExporter.AnyPrefabsSelected();
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExportMenus.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExportMenus.cs.meta
new file mode 100644
index 0000000..f6c8774
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExportMenus.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 1225978330de5fd4f80065a7e395f675
+timeCreated: 1444833356
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExporter.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExporter.cs
new file mode 100644
index 0000000..ee1fce4
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExporter.cs
@@ -0,0 +1,200 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Improbable.Assets;
+using Improbable.Unity.EditorTools.Build;
+using Improbable.Unity.EditorTools.Util;
+using Improbable.Unity.Util;
+using UnityEditor;
+
+namespace Improbable.Unity.EditorTools.PrefabExport
+{
+ public class EntityPrefabExporter
+ {
+ private static readonly Dictionary buildTargetToBuildPlatform =
+ new Dictionary
+ {
+ { BuildTarget.StandaloneWindows, Platform.BuildPlatform.Windows },
+ { BuildTarget.StandaloneWindows64, Platform.BuildPlatform.Windows },
+#if UNITY_2017_3_OR_NEWER
+ { BuildTarget.StandaloneOSX, Platform.BuildPlatform.OSX },
+#else
+ { BuildTarget.StandaloneOSXIntel, Platform.BuildPlatform.OSX },
+ { BuildTarget.StandaloneOSXIntel64, Platform.BuildPlatform.OSX },
+ { BuildTarget.StandaloneOSXUniversal, Platform.BuildPlatform.OSX },
+#endif
+ { BuildTarget.StandaloneLinux, Platform.BuildPlatform.Linux },
+ { BuildTarget.StandaloneLinux64, Platform.BuildPlatform.Linux },
+ { BuildTarget.iOS, Platform.BuildPlatform.iOS }
+ };
+
+ ///
+ /// Exports all entity prefabs for Development and Deployment, for each worker type.
+ ///
+ private static void ExportEntityPrefabs(IEnumerable prefabGuids, IEnumerable buildTargets)
+ {
+ EnsureDirectoriesExist();
+
+ var guids = prefabGuids.ToList();
+
+ foreach (var target in buildTargets)
+ {
+ ExportEntityPrefabs(guids, target);
+ }
+ }
+
+ private static void EnsureDirectoriesExist()
+ {
+ PathUtil.EnsureDirectoryExists(EditorPaths.PrefabExportDirectory);
+ PathUtil.EnsureDirectoryExists(EditorPaths.PrefabSourceDirectory);
+ }
+
+ public static void ExportEntityPrefabs(IEnumerable prefabGuids, BuildTarget buildTarget)
+ {
+ RemoveAssetBundleNamesFromAllPrefabs();
+ AddAssetBundleNamesToPrefabs(prefabGuids, buildTarget);
+ AssetDatabase.SaveAssets();
+ ExportAllNamedPrefabs(buildTarget);
+ RemoveAssetBundleNamesFromAllPrefabs();
+ }
+
+ public static void ExportAllEntityPrefabs()
+ {
+ var buildTargets = GetAllBuildTargets();
+ ExportEntityPrefabs(GetAllPrefabAssetGuids(), buildTargets);
+ }
+
+ public static void ExportAllEntityPrefabs(BuildTarget target)
+ {
+ ExportEntityPrefabs(GetAllPrefabAssetGuids(), new List { target });
+ }
+
+ public static void ExportDevelopmentEntityPrefabs()
+ {
+ var targets = UnityPlayerBuilders.DevelopmentPlayerBuilders(SimpleBuildSystem.AllWorkerTypes)
+ .Select(b => b.BuildTarget)
+ .Distinct()
+ .ToList();
+ ExportEntityPrefabs(GetAllPrefabAssetGuids(), targets);
+ }
+
+ public static void ExportSelectedEntityPrefabs()
+ {
+ var buildTargets = GetAllBuildTargets();
+ ExportEntityPrefabs(GetSelectedPrefabAssetGuids(), buildTargets);
+ }
+
+ public static void ExportSelectedEntityPrefabs(BuildTarget buildTarget)
+ {
+ ExportEntityPrefabs(GetSelectedPrefabAssetGuids(), new List { buildTarget });
+ }
+
+ public static void ExportSelectedDevelopmentEntityPrefabs()
+ {
+ var targets = UnityPlayerBuilders.DevelopmentPlayerBuilders(SimpleBuildSystem.AllWorkerTypes)
+ .Select(b => b.BuildTarget)
+ .Distinct()
+ .ToList();
+ ExportEntityPrefabs(GetSelectedPrefabAssetGuids(), targets);
+ }
+
+ public static IEnumerable GetAllPrefabAssetGuids()
+ {
+ List dirsToSearch = new List { EditorPaths.PrefabSourceDirectory };
+
+ if (Directory.Exists(EditorPaths.PrefabResourcesDirectory))
+ {
+ dirsToSearch.Add(EditorPaths.PrefabResourcesDirectory);
+ }
+
+ return AssetDatabase.FindAssets("t:prefab", dirsToSearch.ToArray()).Where(IsPrefab);
+ }
+
+ public static IEnumerable GetSelectedPrefabAssetGuids()
+ {
+ return GetAllPrefabAssetGuids().Where(s => Selection.assetGUIDs.Contains(s));
+ }
+
+ public static bool AnyPrefabsSelected()
+ {
+ for (var i = 0; i < Selection.assetGUIDs.Length; i++)
+ {
+ var guid = Selection.assetGUIDs[i];
+ var path = AssetDatabase.GUIDToAssetPath(guid);
+ if (path.EndsWith(".prefab") && (path.StartsWith(EditorPaths.PrefabSourceDirectory) || path.StartsWith(EditorPaths.PrefabResourcesDirectory)))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static IList GetAllBuildTargets()
+ {
+ var allTargets = UnityPlayerBuilders.DeploymentPlayerBuilders(SimpleBuildSystem.AllWorkerTypes)
+ .Union(UnityPlayerBuilders.DevelopmentPlayerBuilders(SimpleBuildSystem.AllWorkerTypes))
+ .Select(b => b.BuildTarget)
+ .Distinct()
+ .ToList();
+
+ // Only export iOS prefabs if building for iOS.
+ if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS)
+ {
+ allTargets.Add(BuildTarget.iOS);
+ }
+
+ return allTargets;
+ }
+
+ private static bool IsPrefab(string guid)
+ {
+ return AssetDatabase.GUIDToAssetPath(guid).EndsWith(".prefab");
+ }
+
+ private static void ExportAllNamedPrefabs(BuildTarget buildTarget)
+ {
+ BuildPipeline.BuildAssetBundles(EditorPaths.PrefabExportDirectory,
+ BuildAssetBundleOptions.UncompressedAssetBundle,
+ buildTarget);
+ }
+
+ private static void AddAssetBundleNamesToPrefabs(IEnumerable guids, BuildTarget buildTarget)
+ {
+ if (!buildTargetToBuildPlatform.ContainsKey(buildTarget))
+ {
+ throw new ArgumentException(string.Format("No build platform is associated with build target {0}.", buildTarget));
+ }
+
+ var buildPlatform = buildTargetToBuildPlatform[buildTarget];
+
+ foreach (var guid in guids)
+ {
+ var path = AssetDatabase.GUIDToAssetPath(guid);
+ var importer = AssetImporter.GetAtPath(path);
+ var name = Path.GetFileNameWithoutExtension(path);
+ if (name == null)
+ {
+ throw new InvalidOperationException(string.Format("Asset path is invalid {0}", path));
+ }
+
+ importer.assetBundleName = string.Format("{0}@{1}", name.ToLowerInvariant(), Platform.BuildPlatformToAssetBundleSuffix(buildPlatform));
+ }
+ }
+
+ private static void RemoveAssetBundleNamesFromAllPrefabs()
+ {
+ var prefabGuids = GetAllPrefabAssetGuids();
+ foreach (var guid in prefabGuids)
+ {
+ var path = AssetDatabase.GUIDToAssetPath(guid);
+ var importer = AssetImporter.GetAtPath(path);
+ importer.assetBundleName = null;
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExporter.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExporter.cs.meta
new file mode 100644
index 0000000..abac56d
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/PrefabExport/EntityPrefabExporter.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ef89bc6190d1ca2418b44113dbf5eb25
+timeCreated: 1444833363
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util.meta
new file mode 100644
index 0000000..7383eb6
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 21374d5b0c6d381418c4499ab5b65ad4
+folderAsset: yes
+timeCreated: 1444833351
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorPaths.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorPaths.cs
new file mode 100644
index 0000000..300cd17
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorPaths.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+
+using Improbable.Unity.Util;
+
+namespace Improbable.Unity.EditorTools.Util
+{
+ ///
+ /// Contains common directories related to building assets and players.
+ ///
+ ///
+ /// All directories should be in Unity path format e.g. "Foo/Bar".
+ ///
+ public static class EditorPaths
+ {
+ public static readonly string OrganizationName = "Improbable";
+
+ public static readonly string PluginDirectory = PathUtil.Combine("Assets", "Plugins").ToUnityPath();
+
+ public static readonly string DataDirectory = PathUtil.Combine("..", "..", "build").ToUnityPath();
+ public static readonly string AssetDatabaseDirectory = PathUtil.Combine(DataDirectory, "assembly").ToUnityPath();
+ public static readonly string AssetDirectory = PathUtil.Combine(PluginDirectory, OrganizationName).ToUnityPath();
+
+ public static readonly string PrefabCompileDirectory = PathUtil.Combine(AssetDirectory, "EntityPrefabs").ToUnityPath();
+ public static readonly string PrefabExportDirectory = PathUtil.Combine(AssetDatabaseDirectory, "unity").ToUnityPath();
+ public static readonly string PrefabResourcesDirectory = PathUtil.Combine("Assets", "Resources", "EntityPrefabs").ToUnityPath();
+ public static readonly string PrefabSourceDirectory = PathUtil.Combine("Assets", "EntityPrefabs").ToUnityPath();
+
+ public static readonly string ScriptAssembliesDirectory = PathUtil.Combine("Library", "ScriptAssemblies").ToUnityPath();
+
+ public static readonly string CodeGeneratorScratchDirectory = ".spatialos";
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorPaths.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorPaths.cs.meta
new file mode 100644
index 0000000..5514371
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorPaths.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 32a181b1f3584f94caf51ac91bd7005b
+timeCreated: 1444833357
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorTaskRunnerWithRetries.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorTaskRunnerWithRetries.cs
new file mode 100644
index 0000000..48bd34c
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorTaskRunnerWithRetries.cs
@@ -0,0 +1,88 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Util
+{
+ ///
+ /// Utility class for running tasks with timestamp based retries. Suitable for situations where coroutines are not
+ /// available.
+ ///
+ internal static class EditorTaskRunnerWithRetries
+ {
+ public const int DefaultNumRetries = 1;
+ public static readonly TimeSpan RetryPeriod = TimeSpan.FromSeconds(10);
+
+ private static Queue failedTaskRetryQueue = new Queue();
+ private static IList bufferedExceptions = new List();
+
+ public static void RunTask(Action task, int retries = DefaultNumRetries, bool throwExceptions = true)
+ {
+ try
+ {
+ task();
+ }
+ catch
+ {
+ if (retries > 0)
+ {
+ Retry(task, retries - 1, throwExceptions);
+ }
+ else
+ {
+ if (throwExceptions)
+ {
+ throw;
+ }
+ }
+ }
+ }
+
+ private static void Retry(Action task, int remainingRetries, bool throwExceptions)
+ {
+ if (failedTaskRetryQueue.Count == 0)
+ {
+ EditorApplication.update += EditorUpdate;
+ }
+
+ var failedTask = new FailedEditorTask(task, EditorApplication.timeSinceStartup, remainingRetries, throwExceptions);
+ failedTaskRetryQueue.Enqueue(failedTask);
+ }
+
+ private static void EditorUpdate()
+ {
+ bufferedExceptions.Clear();
+
+ while (failedTaskRetryQueue.Count > 0 && RetryPeriodHasElapsed(failedTaskRetryQueue.Peek()))
+ {
+ var failedTask = failedTaskRetryQueue.Dequeue();
+ try
+ {
+ RunTask(failedTask.Task, failedTask.RemainingRetries, failedTask.ShouldThrowExceptions);
+ }
+ catch (Exception e)
+ {
+ bufferedExceptions.Add(e);
+ }
+ }
+
+ if (failedTaskRetryQueue.Count == 0)
+ {
+ EditorApplication.update -= EditorUpdate;
+ }
+
+ for (var i = 0; i < bufferedExceptions.Count; i++)
+ {
+ Debug.LogException(bufferedExceptions[i]);
+ }
+ }
+
+ private static bool RetryPeriodHasElapsed(FailedEditorTask task)
+ {
+ return task.TimeStamp <= EditorApplication.timeSinceStartup - RetryPeriod.Seconds;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorTaskRunnerWithRetries.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorTaskRunnerWithRetries.cs.meta
new file mode 100644
index 0000000..30cd149
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/EditorTaskRunnerWithRetries.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 7649f402bc6dfaf4faf500e433976e00
+timeCreated: 1504692725
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/FailedEditorTask.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/FailedEditorTask.cs
new file mode 100644
index 0000000..cbe199c
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/FailedEditorTask.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+
+namespace Improbable.Unity.EditorTools.Util
+{
+ ///
+ /// Utility companion class for EditorTaskRunnerWithRetries.
+ ///
+ internal class FailedEditorTask
+ {
+ public readonly Action Task;
+ public readonly double TimeStamp;
+ public readonly int RemainingRetries;
+ public readonly bool ShouldThrowExceptions;
+
+ public FailedEditorTask(Action task, double timeStamp, int remainingRetries, bool shouldThrowExceptions)
+ {
+ Task = task;
+ TimeStamp = timeStamp;
+ RemainingRetries = remainingRetries;
+ ShouldThrowExceptions = shouldThrowExceptions;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/FailedEditorTask.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/FailedEditorTask.cs.meta
new file mode 100644
index 0000000..0e63ebb
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/FailedEditorTask.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 6c96b3bc8049bb048b999980e953d21b
+timeCreated: 1504703108
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/GuiUtil.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/GuiUtil.cs
new file mode 100644
index 0000000..519301d
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/GuiUtil.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Util
+{
+ public class GuiUtil
+ {
+ public static readonly GUIStyle GreenTextStyle = new GUIStyle { normal = { textColor = Color.green } };
+ public static readonly GUIStyle RedTextStyle = new GUIStyle { normal = { textColor = Color.red } };
+ public static readonly GUIStyle YellowTextStyle = new GUIStyle { normal = { textColor = Color.yellow } };
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/GuiUtil.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/GuiUtil.cs.meta
new file mode 100644
index 0000000..111e0ca
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/GuiUtil.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 74ae124032a5d7141b61d73ca257b0ed
+timeCreated: 1485356049
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/JsonUtil.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/JsonUtil.cs
new file mode 100644
index 0000000..3e2797f
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/JsonUtil.cs
@@ -0,0 +1,81 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace Improbable.Unity.EditorTools.Util
+{
+ internal static class JsonUtil
+ {
+ ///
+ /// Return a JSON string representation of an IDictionary.
+ ///
+ internal static string ToJson(this IDictionary dict)
+ {
+ if (dict == null || dict.Count == 0)
+ {
+ return "{}";
+ }
+
+ var stringBuilder = new StringBuilder();
+ stringBuilder.Append('{');
+ stringBuilder.Append(string.Join(",", dict.Select(pair => string.Format("\"{0}\":\"{1}\"", Escape(pair.Key), Escape(pair.Value))).ToArray()));
+ stringBuilder.Append('}');
+ return stringBuilder.ToString();
+ }
+
+ ///
+ /// Escapes the given text string for use in JSON.
+ ///
+ ///
+ /// Escapes characters based on the ECMA-404 Standard (JSON.org)
+ /// http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
+ ///
+ internal static string Escape(string text)
+ {
+ StringWriter stringWriter = new StringWriter();
+ foreach (var c in text)
+ {
+ switch (c)
+ {
+ case '"':
+ stringWriter.Write("\\\"");
+ break;
+ case '\\':
+ stringWriter.Write("\\\\");
+ break;
+ case '\b':
+ stringWriter.Write("\\b");
+ break;
+ case '\f':
+ stringWriter.Write("\\f");
+ break;
+ case '\n':
+ stringWriter.Write("\\n");
+ break;
+ case '\r':
+ stringWriter.Write("\\r");
+ break;
+ case '\t':
+ stringWriter.Write("\\t");
+ break;
+ default:
+ if ('\x00' <= c && c <= '\x1f')
+ {
+ stringWriter.Write("\\u" + string.Format("{0:X}", (int) c).PadLeft(4, '0'));
+ }
+ else
+ {
+ stringWriter.Write(c);
+ }
+
+ break;
+ }
+ }
+
+ return stringWriter.ToString();
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/JsonUtil.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/JsonUtil.cs.meta
new file mode 100644
index 0000000..d882dba
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/JsonUtil.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 59daeb460721dcd4e8eff8b8698a317c
+timeCreated: 1504117491
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/SerializableDictionary.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/SerializableDictionary.cs
new file mode 100644
index 0000000..5333240
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/SerializableDictionary.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Improbable.Unity.EditorTools.Util
+{
+ ///
+ /// Unity's serialization system can't handle Dictionary by default.
+ /// Users must inherit their from this type to serialize it.
+ ///
+ [Serializable]
+ internal class SerializableDictionary : Dictionary, ISerializationCallbackReceiver
+ {
+ // These fields must not be readonly, or serialization will not work.
+
+ // ReSharper disable once FieldCanBeMadeReadOnly.Local
+ [SerializeField] private List keys = new List();
+
+ // ReSharper disable once FieldCanBeMadeReadOnly.Local
+ [SerializeField] private List values = new List();
+
+ public void OnBeforeSerialize()
+ {
+ keys.Clear();
+ values.Clear();
+
+ foreach (var pair in this)
+ {
+ keys.Add(pair.Key);
+ values.Add(pair.Value);
+ }
+ }
+
+ public void OnAfterDeserialize()
+ {
+ Clear();
+
+ if (keys.Count != values.Count)
+ {
+ throw new Exception(string.Format("there are {0} keys and {1} values after deserialization. Make sure that both key and value types are [Serializable].", keys.Count, values.Count));
+ }
+
+ for (int i = 0; i < keys.Count; i++)
+ {
+ Add(keys[i], values[i]);
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/SerializableDictionary.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/SerializableDictionary.cs.meta
new file mode 100644
index 0000000..4f0a3da
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/SerializableDictionary.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 838b03387e0ed71409cd7ec55a037abe
+timeCreated: 1494600098
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/TempFolder.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/TempFolder.cs
new file mode 100644
index 0000000..ef425f1
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/TempFolder.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.IO;
+
+namespace Improbable.Unity.EditorTools.Util
+{
+ public class TempFolder : IDisposable
+ {
+ private string path;
+
+ public String Path
+ {
+ get
+ {
+ if (!Initialized)
+ {
+ path = CreateTempPath();
+ }
+
+ return path;
+ }
+ }
+
+ private bool Initialized
+ {
+ get { return !string.IsNullOrEmpty(path); }
+ }
+
+ public void Dispose()
+ {
+ if (Initialized)
+ {
+ Directory.Delete(path, true);
+ }
+ }
+
+ public string CreateTempPath()
+ {
+ string tempDirectory = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());
+ Directory.CreateDirectory(tempDirectory);
+ return tempDirectory;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/TempFolder.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/TempFolder.cs.meta
new file mode 100644
index 0000000..5cf5ada
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/TempFolder.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 2cb066212a2c59b43a2301146a783a34
+timeCreated: 1444833357
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/UnityPathUtil.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/UnityPathUtil.cs
new file mode 100644
index 0000000..26a8779
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/UnityPathUtil.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.IO;
+using Improbable.Unity.Util;
+
+namespace Improbable.Unity.EditorTools.Util
+{
+ public static class UnityPathUtil
+ {
+ public static void EnsureDirectoryClean(string directory)
+ {
+ EnsureDirectoryRemoved(directory);
+ PathUtil.EnsureDirectoryExists(directory);
+ }
+
+ public static void EnsureDirectoryRemoved(string directory)
+ {
+ if (Directory.Exists(directory))
+ {
+ var directoryInfo = new DirectoryInfo(directory);
+
+ foreach (var file in directoryInfo.GetFiles("*", SearchOption.AllDirectories))
+ {
+ file.Attributes &= ~FileAttributes.ReadOnly;
+ }
+
+ Directory.Delete(directory, true);
+ DeleteMetaFile(directory);
+ }
+ }
+
+ public static void EnsureFileRemoved(string file)
+ {
+ if (File.Exists(file))
+ {
+ DeleteFile(file);
+ }
+ }
+
+ public static void DeleteFile(string file)
+ {
+ File.Delete(file);
+ DeleteMetaFile(file);
+ }
+
+ public static void DeleteMetaFile(string path)
+ {
+ var metaPath = path + ".meta";
+ if (File.Exists(metaPath))
+ {
+ File.Delete(metaPath);
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/UnityPathUtil.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/UnityPathUtil.cs.meta
new file mode 100644
index 0000000..67912cd
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Editor/Util/UnityPathUtil.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: bd8f01b859b3f9f459016921f8d78e24
+timeCreated: 1454508522
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity.meta
new file mode 100644
index 0000000..7abf1e5
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 6476a33cc249f3d4c9d26dea32a4c757
+folderAsset: yes
+timeCreated: 1526049922
+licenseType: Pro
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets.meta
new file mode 100644
index 0000000..d813c70
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: c8ad01ea56b15c849aea76f8ac408cb5
+folderAsset: yes
+timeCreated: 1444833351
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetBundleDownloader.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetBundleDownloader.cs
new file mode 100644
index 0000000..a396361
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetBundleDownloader.cs
@@ -0,0 +1,294 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Serialization;
+using Improbable.Assets;
+using Improbable.Unity.Util;
+using UnityEngine;
+
+namespace Improbable.Unity.Assets
+{
+ public class AssetBundleDownloader : IAssetLoader
+ {
+ private IMachineCache assetCache;
+ private IMachineCache metaDataCache;
+ private string cachePath;
+ private IWWWRequest wwwRequest;
+ private MonoBehaviour coroutineHost;
+
+ private readonly HashSet pendingDownloadRequests = new HashSet();
+
+ ///
+ /// The method to invoke to resolve a prefab name to a URL. Throws a if the asset
+ /// is unknown.
+ ///
+ public Func GetAssetUrl { get; set; }
+
+ public AssetBundleDownloader()
+ {
+ GetAssetUrl = s => { throw new InvalidOperationException("GetAssetUrl has not been assigned."); };
+ }
+
+ internal AssetBundleDownloader(string cachePath, IMachineCache assetCache, IMachineCache metaDataCache, IWWWRequest wwwRequest, MonoBehaviour coroutineHost)
+ {
+ this.cachePath = cachePath;
+ this.assetCache = assetCache;
+ this.metaDataCache = metaDataCache;
+ this.wwwRequest = wwwRequest;
+ this.coroutineHost = coroutineHost;
+ }
+
+ ///
+ public void LoadAsset(string prefabName, Action onAssetLoaded, Action onError)
+ {
+ try
+ {
+ var assetUri = GetAssetUrl(prefabName);
+
+ Action callback = (response) => { HandleDownloadAssetResponse(assetUri, onAssetLoaded, onError, response); };
+
+ DownloadAsync(wwwRequest, assetUri, assetUri, callback);
+ }
+ catch (Exception ex)
+ {
+ onError(ex);
+ }
+ }
+
+ public void CancelAllLoads()
+ {
+ foreach (var requestId in pendingDownloadRequests)
+ {
+ requestId.Cancel();
+ }
+
+ pendingDownloadRequests.Clear();
+ }
+
+ private void HandleDownloadAssetResponse(string url, Action onAssetLoaded, Action onError, WWWResponse response)
+ {
+ if (!String.IsNullOrEmpty(response.Error))
+ {
+ Debug.LogWarningFormat("Requesting asset '{0}' failed. Url: {1}. Error: {2}", GetAssetName(url), url, response.Error);
+ onError(new ApplicationException(response.Error));
+ }
+ else if (response.ResponseHeaders.ContainsKey("STATUS") && response.ResponseHeaders["STATUS"].Contains("304"))
+ {
+ AssetBundle assetBundle;
+ if (assetCache.TryGet(GetAssetName(url), out assetBundle))
+ {
+ onAssetLoaded(assetBundle);
+ }
+ else
+ {
+ Debug.LogErrorFormat("Failed to load asset '{0}' from cache. Corrupted cache. Please delete the cache folder: '{1}'.", GetAssetName(url), cachePath);
+ onError(new ApplicationException("Cache likely corrupted."));
+ }
+ }
+ else if (response.AssetBundle != null)
+ {
+ UpdateCache(url, response);
+ onAssetLoaded(response.AssetBundle);
+ }
+ else
+ {
+ string responseHeadersString = "";
+ foreach (var responseHeader in response.ResponseHeaders)
+ {
+ responseHeadersString += " [" + responseHeader.Key + ": " + responseHeader.Value + "]";
+ }
+
+ Debug.LogWarningFormat("Unhandled response for {0}. Downloaded {1} bytes, got the following response headers: {2}", url, response.BytesDownloaded, responseHeadersString);
+ onError(new ApplicationException("Unhandled response."));
+ }
+ }
+
+ private static string GetAssetName(string assetUrl)
+ {
+ return new Uri(assetUrl).PathAndQuery;
+ }
+
+ private void UpdateCache(string url, WWWResponse response)
+ {
+ var added = assetCache.TryAddOrUpdate(GetAssetName(url), response.Bytes);
+
+ if (added)
+ {
+ AddMetaDataCacheEntry(url, response);
+ }
+ else
+ {
+ Debug.LogWarningFormat("Failed to cache: {0}", url);
+ }
+ }
+
+ private void AddMetaDataCacheEntry(string url, WWWResponse response)
+ {
+ if (!HasValidators(response))
+ {
+ Debug.LogWarningFormat("ETag and/or Last-Modified missing. Cannot cache url {0}, keys {1}", url, string.Join("\n", response.ResponseHeaders.Keys.ToArray()));
+ }
+ else
+ {
+ var entry = CreateMetaDataCacheEntry(url, response);
+ if (!metaDataCache.TryAddOrUpdate(GetAssetName(url), entry))
+ {
+ Debug.LogWarningFormat("Failed to add cache metadata for: {0}", url);
+ }
+ }
+ }
+
+ private static bool HasValidators(WWWResponse response)
+ {
+ return response.ResponseHeaders.ContainsKey("LAST-MODIFIED") && response.ResponseHeaders.ContainsKey("ETAG");
+ }
+
+ private static CacheEntry CreateMetaDataCacheEntry(string url, WWWResponse response)
+ {
+ string responseHeader = response.ResponseHeaders["LAST-MODIFIED"];
+ string entityTag = response.ResponseHeaders["ETAG"];
+ var cacheEntry = new CacheEntry { EntityTag = entityTag, Modified = responseHeader, Url = url, LastFetched = DateTime.UtcNow };
+ return cacheEntry;
+ }
+
+ private void DownloadAsync(IWWWRequest wwwRequest, string originalUrl, string redirectedUrl, Action callback)
+ {
+ WWWRequestWrapper requestWrapper;
+
+ // It is possible for 'SendGetRequest' or 'SendPostRequest' to have completed immediately.
+ // If this is the case, then we don't mark the request as pending.
+ var responseReceived = false;
+
+ Action responseHandler = (response) =>
+ {
+ responseReceived = true;
+ DownloadAsyncResponse(response, wwwRequest, originalUrl, callback);
+ };
+
+ var headers = GetCacheHeaders(originalUrl);
+
+ if (headers == null)
+ {
+ requestWrapper = wwwRequest.SendGetRequest(coroutineHost, redirectedUrl, responseHandler);
+ }
+ else
+ {
+ requestWrapper = wwwRequest.SendPostRequest(coroutineHost, redirectedUrl, null, headers, responseHandler);
+ }
+
+ if (!responseReceived)
+ {
+ pendingDownloadRequests.Add(requestWrapper);
+ }
+ }
+
+ private void DownloadAsyncResponse(WWWResponse response, IWWWRequest wwwRequest, string originalUrl, Action callback)
+ {
+ pendingDownloadRequests.Remove(response.Request);
+
+ var redirectUrl = GetRedirectUrl(response);
+ if (redirectUrl != null)
+ {
+ DownloadAsync(wwwRequest, originalUrl, redirectUrl, callback);
+ }
+ else
+ {
+ callback(response);
+ }
+ }
+
+ private Dictionary GetCacheHeaders(string url)
+ {
+ CacheEntry entry;
+ if (metaDataCache.TryGet(GetAssetName(url), out entry))
+ {
+ return new Dictionary
+ {
+ { "If-Modified-Since", entry.Modified },
+ { "If-None-Match", entry.EntityTag },
+ };
+ }
+
+ return null;
+ }
+
+ private static string GetRedirectUrl(WWWResponse response)
+ {
+ string status = "";
+ if (response.ResponseHeaders.TryGetValue("STATUS", out status))
+ {
+ if (status.Contains("302"))
+ {
+ string location = "";
+ if (response.ResponseHeaders.TryGetValue("LOCATION", out location))
+ {
+ return location;
+ }
+ else
+ {
+ Debug.LogErrorFormat("Requested {0} and got a 302, but no location header", response.Url);
+ }
+ }
+ }
+
+ return null;
+ }
+ }
+
+ public class AssetBundlePersistenceStrategy : MachineCache.IPersistenceStrategy
+ {
+ public void WriteToCacheFile(string outputCacheFile, byte[] resource)
+ {
+ File.WriteAllBytes(outputCacheFile, resource);
+ }
+
+ public AssetBundle ReadFromCacheFile(string inputCacheFile)
+ {
+ return AssetBundle.LoadFromFile(inputCacheFile);
+ }
+ }
+
+ public class AssetMetadataPersistenceStrategy : MachineCache.IPersistenceStrategy
+ {
+ private static readonly XmlSerializer metaDataSerializer = new XmlSerializer(typeof(Container));
+
+ public CacheEntry ReadFromCacheFile(string filename)
+ {
+ using (var file = File.OpenRead(filename))
+ {
+ var container = (Container) metaDataSerializer.Deserialize(file);
+ return container.CacheEntry;
+ }
+ }
+
+ public void WriteToCacheFile(string filename, CacheEntry entry)
+ {
+ using (var file = File.CreateText(filename))
+ {
+ var container = new Container
+ {
+ CacheEntry = entry
+ };
+ metaDataSerializer.Serialize(file, container);
+ }
+ }
+ }
+
+ public class CacheEntry
+ {
+ [XmlAttribute] public string EntityTag;
+
+ [XmlAttribute] public DateTime LastFetched;
+ [XmlAttribute] public string Modified;
+ [XmlAttribute] public string Url;
+ }
+
+ [XmlRoot("Cache")]
+ public class Container
+ {
+ [XmlElement] public CacheEntry CacheEntry;
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetBundleDownloader.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetBundleDownloader.cs.meta
new file mode 100644
index 0000000..0a06d94
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetBundleDownloader.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 12cf77239e0cbef44950eec439e7fc87
+timeCreated: 1444833356
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetDatabaseStrategy.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetDatabaseStrategy.cs
new file mode 100644
index 0000000..df97092
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetDatabaseStrategy.cs
@@ -0,0 +1,10 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+namespace Improbable.Unity.Assets
+{
+ public enum AssetDatabaseStrategy
+ {
+ Local,
+ Streaming
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetDatabaseStrategy.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetDatabaseStrategy.cs.meta
new file mode 100644
index 0000000..acecf47
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/AssetDatabaseStrategy.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 48117de27aec83446a8ee791e0465765
+timeCreated: 1455879378
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/BehaviourWorkerCompatibilityCache.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/BehaviourWorkerCompatibilityCache.cs
new file mode 100644
index 0000000..35b21a9
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/BehaviourWorkerCompatibilityCache.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Improbable.Unity.Visualizer;
+using UnityEngine;
+
+namespace Improbable.Unity.Assets
+{
+ class BehaviourWorkerCompatibilityCache
+ {
+ private readonly HashSet compatibleBehaviours;
+
+ public BehaviourWorkerCompatibilityCache(WorkerPlatform platform)
+ {
+ compatibleBehaviours = new HashSet();
+ var allTypes = AppDomain
+ .CurrentDomain
+ .GetAssemblies()
+ .SelectMany(assembly => assembly.GetTypes());
+ foreach (var type in allTypes)
+ {
+ var attributes = type.GetCustomAttributes(typeof(WorkerTypeAttribute), false);
+ if (typeof(MonoBehaviour).IsAssignableFrom(type))
+ {
+ if (IsPlatformCompatible(attributes, platform))
+ {
+ compatibleBehaviours.Add(type);
+ }
+ }
+ else if (attributes.Length > 0)
+ {
+ Debug.LogWarningFormat("{0} uses EngineTypeAttribute but is not MonoBehavoiur. The attribute will be ignored.", type.FullName);
+ }
+ }
+ }
+
+ public bool IsCompatibleBehaviour(Type behaviourType)
+ {
+ return compatibleBehaviours.Contains(behaviourType);
+ }
+
+ private static bool IsPlatformCompatible(object[] workerTypes, WorkerPlatform platform)
+ {
+ WorkerPlatform workerPlatformMask = 0;
+ for (int i = 0; i < workerTypes.Length; i++)
+ {
+ workerPlatformMask |= ((WorkerTypeAttribute) workerTypes[i]).WorkerPlatform;
+ }
+
+ return workerTypes.Length == 0 || (workerPlatformMask & platform) != 0;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/BehaviourWorkerCompatibilityCache.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/BehaviourWorkerCompatibilityCache.cs.meta
new file mode 100644
index 0000000..f8bd887
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/BehaviourWorkerCompatibilityCache.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5235339c146a675449098fb3ffe85a40
+timeCreated: 1448883129
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/CachingAssetDatabase.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/CachingAssetDatabase.cs
new file mode 100644
index 0000000..265590f
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/CachingAssetDatabase.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using Improbable.Assets;
+using UnityEngine;
+
+namespace Improbable.Unity.Assets
+{
+ public class CachingAssetDatabase : IAssetDatabase
+ {
+ private readonly IDictionary cachedGameObjects = new Dictionary();
+ private readonly IAssetLoader gameObjectLoader;
+
+ public CachingAssetDatabase(IAssetLoader gameObjectLoader)
+ {
+ this.gameObjectLoader = gameObjectLoader;
+ }
+
+ public void LoadAsset(string prefabName, Action onAssetLoaded, Action onError)
+ {
+ GameObject cachedGameObject;
+ if (cachedGameObjects.TryGetValue(prefabName, out cachedGameObject))
+ {
+ onAssetLoaded(cachedGameObject);
+ }
+ else
+ {
+ gameObjectLoader.LoadAsset(prefabName, gameObject =>
+ {
+ if (!cachedGameObjects.ContainsKey(prefabName))
+ {
+ cachedGameObjects.Add(prefabName, gameObject);
+ }
+
+ onAssetLoaded(gameObject);
+ }, onError);
+ }
+ }
+
+ public void CancelAllLoads()
+ {
+ gameObjectLoader.CancelAllLoads();
+ }
+
+ public bool TryGet(string prefabName, out GameObject prefabGameObject)
+ {
+ return cachedGameObjects.TryGetValue(prefabName, out prefabGameObject);
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/CachingAssetDatabase.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/CachingAssetDatabase.cs.meta
new file mode 100644
index 0000000..93873b3
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/CachingAssetDatabase.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 8377b044344f0ba4797a82b5d61bacd8
+timeCreated: 1444833360
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/DefaultTemplateProvider.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/DefaultTemplateProvider.cs
new file mode 100644
index 0000000..9b6ae5f
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/DefaultTemplateProvider.cs
@@ -0,0 +1,197 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Improbable.Assets;
+using Improbable.Unity.Assets;
+using Improbable.Unity.Configuration;
+using Improbable.Unity.Core;
+using Improbable.Unity.Util;
+using UnityEngine;
+
+namespace Improbable.Unity.Entity
+{
+ ///
+ /// The DefaultTemplateProvider switches between three strategies, based on whether it's running in the editor, is
+ /// configured to use local prefabs or has a streaming strategy set.
+ ///
+ public class DefaultTemplateProvider : MonoBehaviour, IEntityTemplateProvider
+ {
+ private Dictionary assetBundleNameToUrl;
+ private List pendingPrepareTemplates;
+
+ // These can be overridden on the command line.
+ public bool UseLocalPrefabs;
+ public string LocalAssetDatabasePath = "../../build/assembly/";
+ protected AssetDatabaseStrategy LoadingStrategy;
+
+ // The template provider can't be instantiated during construction as Application.isEditor doesn't work.
+ private IEntityTemplateProvider templateProvider;
+
+ private CloudAssemblyArtifactResolver pendingUrlResolveRequest = null;
+
+ private IEntityTemplateProvider TemplateProvider
+ {
+ get
+ {
+ if (templateProvider == null)
+ {
+ var gameObjectLoader = InitializeAssetLoader();
+ templateProvider = InitializeTemplateProvider(gameObjectLoader);
+ }
+
+ return templateProvider;
+ }
+ }
+
+ protected virtual IAssetLoader InitializeAssetLoader()
+ {
+ var projectName = SpatialOS.Configuration.ProjectName;
+ var deployment = SpatialOS.Deployment;
+ var assemblyName = deployment.HasValue ? deployment.Value.AssemblyName : SpatialOS.Configuration.AssemblyName;
+
+ // If an assembly name is set, default to streaming from it. The strategy can still be overridden by the command line.
+ AssetDatabaseStrategy defaultStrategy = AssetDatabaseStrategy.Local;
+ if (!string.IsNullOrEmpty(projectName) && !string.IsNullOrEmpty(assemblyName))
+ {
+ defaultStrategy = AssetDatabaseStrategy.Streaming;
+ }
+
+ UseLocalPrefabs = SpatialOS.Configuration.GetCommandLineValue(CommandLineConfigNames.UseLocalPrefabs, UseLocalPrefabs);
+ LoadingStrategy = SpatialOS.Configuration.GetCommandLineValue(CommandLineConfigNames.AssetDatabaseStrategy, defaultStrategy);
+ LocalAssetDatabasePath = SpatialOS.Configuration.GetCommandLineValue(CommandLineConfigNames.LocalAssetDatabasePath, LocalAssetDatabasePath);
+
+ IAssetLoader gameObjectLoader;
+
+ if (Application.isEditor && UseLocalPrefabs)
+ {
+ gameObjectLoader = new PrefabGameObjectLoader();
+ }
+ else
+ {
+ switch (LoadingStrategy)
+ {
+ case AssetDatabaseStrategy.Local:
+ var path = Path.GetFullPath(LocalAssetDatabasePath);
+ gameObjectLoader = new GameObjectFromAssetBundleLoader(new LocalAssetBundleLoader(path));
+ break;
+ case AssetDatabaseStrategy.Streaming:
+
+ pendingPrepareTemplates = new List();
+
+ var cachePath = Path.Combine(Application.persistentDataPath, "cache" + WorkerTypeUtils.ToWorkerName(SpatialOS.Configuration.WorkerPlatform));
+ Directory.CreateDirectory(cachePath);
+ var assetBundleDownloader = new AssetBundleDownloader(cachePath,
+ new MachineCache(Path.Combine(cachePath, "assets"), new AssetBundlePersistenceStrategy()),
+ new MachineCache(Path.Combine(cachePath, "asset-metadata"), new AssetMetadataPersistenceStrategy()),
+ new WWWRequest(),
+ this);
+ assetBundleDownloader.GetAssetUrl = GetAssetUrl;
+
+ var exponentialBackoffRetryAssetLoader = gameObject.GetComponent()
+ ?? gameObject.AddComponent();
+ exponentialBackoffRetryAssetLoader.Init(assetBundleDownloader,
+ SpatialOS.Configuration.GetCommandLineValue(CommandLineConfigNames.MaxAssetLoadingRetries, -1),
+ SpatialOS.Configuration.GetCommandLineValue(CommandLineConfigNames.AssetLoadingRetryBackoffMilliseconds, -1));
+
+ gameObjectLoader = new GameObjectFromAssetBundleLoader(exponentialBackoffRetryAssetLoader);
+ pendingUrlResolveRequest = CloudAssemblyArtifactResolver.ResolveAssetUrls(this, new WWWRequest(), SpatialOS.Configuration.InfraServiceUrl, projectName, assemblyName, OnAssetBundleNameToUrlMapResolved, OnAssetResolveFailed);
+ break;
+ default:
+ throw new Exception(string.Format("Unknown loading strategy '{0}'", LoadingStrategy));
+ }
+ }
+
+ return gameObjectLoader;
+ }
+
+ protected virtual IEntityTemplateProvider InitializeTemplateProvider(IAssetLoader gameObjectLoader)
+ {
+ return new AssetDatabaseTemplateProvider(new CachingAssetDatabase(new PreprocessingGameObjectLoader(gameObjectLoader)));
+ }
+
+ ///
+ public virtual void PrepareTemplate(string prefabName, Action onSuccess, Action onError)
+ {
+ // TemplateProvider is initialized-on-access, so ensure we're all setup before checking pendingPrepareTemplates
+ var provider = TemplateProvider;
+ if (pendingPrepareTemplates != null)
+ {
+ pendingPrepareTemplates.Add(() => provider.PrepareTemplate(prefabName, onSuccess, onError));
+ return;
+ }
+
+ provider.PrepareTemplate(prefabName, onSuccess, onError);
+ }
+
+ public void CancelAllTemplatePreparations()
+ {
+ if (pendingUrlResolveRequest != null)
+ {
+ pendingUrlResolveRequest.Cancel();
+ pendingUrlResolveRequest = null;
+ }
+
+ if (pendingPrepareTemplates != null)
+ {
+ pendingPrepareTemplates = null;
+ return;
+ }
+
+ if (templateProvider != null)
+ {
+ templateProvider.CancelAllTemplatePreparations();
+ }
+ }
+
+ ///
+ public virtual GameObject GetEntityTemplate(string prefabName)
+ {
+ return TemplateProvider.GetEntityTemplate(prefabName);
+ }
+
+ private string GetAssetUrl(string prefabName)
+ {
+ var assetBundleName = Platform.PrefabNameToAssetBundleName(prefabName.ToLowerInvariant());
+ string url;
+ if (!assetBundleNameToUrl.TryGetValue(assetBundleName, out url))
+ {
+ throw new KeyNotFoundException(string.Format("Trying to load a non-existent asset bundle named '{0}'", assetBundleName));
+ }
+
+ return url;
+ }
+
+ private void OnAssetResolveFailed(Exception err)
+ {
+ pendingUrlResolveRequest = null;
+
+ throw err;
+ }
+
+ private void OnAssetBundleNameToUrlMapResolved(Dictionary map)
+ {
+ pendingUrlResolveRequest = null;
+
+ assetBundleNameToUrl = map;
+ InvokePendingPrepareTemplates();
+ }
+
+ private void InvokePendingPrepareTemplates()
+ {
+ if (pendingPrepareTemplates == null)
+ {
+ return;
+ }
+
+ // Start all pending PrepareTemplate requests now that we've finished resolving
+ for (var i = 0; i < pendingPrepareTemplates.Count; i++)
+ {
+ pendingPrepareTemplates[i]();
+ }
+
+ pendingPrepareTemplates = null;
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/DefaultTemplateProvider.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/DefaultTemplateProvider.cs.meta
new file mode 100644
index 0000000..3597bda
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/DefaultTemplateProvider.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5d504af5aa1f0ed4fad51a0dfde196c0
+timeCreated: 1444833358
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/ExponentialBackoffRetryAssetLoader.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/ExponentialBackoffRetryAssetLoader.cs
new file mode 100644
index 0000000..20c0ae2
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/ExponentialBackoffRetryAssetLoader.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using Improbable.Assets;
+using Improbable.Unity.Util;
+using UnityEngine;
+
+namespace Improbable.Unity.Assets
+{
+ class ExponentialBackoffRetryAssetLoader : MonoBehaviour, IAssetLoader
+ {
+ [Range(0, 100)] public int MaxRetries = 3;
+
+ [Range(250, 10000)] public int StartBackoffTimeoutMilliseconds = 250;
+
+ private IAssetLoader assetLoader;
+
+ public void Init(IAssetLoader assetLoader, int maxRetries = -1, int startBackoffTimeoutMilliseconds = -1)
+ {
+ this.assetLoader = assetLoader;
+ MaxRetries = maxRetries != -1 ? maxRetries : MaxRetries;
+ StartBackoffTimeoutMilliseconds = startBackoffTimeoutMilliseconds != -1 ? startBackoffTimeoutMilliseconds : StartBackoffTimeoutMilliseconds;
+ }
+
+ ///
+ public void LoadAsset(string prefabName, Action onAssetLoaded, Action onError)
+ {
+ var taskRunner = new TaskRunnerWithExponentialBackoff();
+ Action runTask = () => { assetLoader.LoadAsset(prefabName, onAssetLoaded, taskRunner.ProcessResult); };
+ Func evaluationFunc = (Exception e) =>
+ {
+ return new TaskResult
+ {
+ IsSuccess = false, // This function should never be called if the load request was successful.
+ ErrorMessage = e.Message
+ };
+ };
+ Action onSuccess = (Exception e) => { throw new InvalidOperationException("ExponentialBackoffRetryAssetLoader: TaskRunnerWithExponentialBackoff::onSuccess was called. This code is not supposed to be reachable."); };
+ Action onFailure = (string errorMessage) => { onError(new Exception(errorMessage)); };
+ taskRunner.RunTaskWithRetries("ExponentialBackoffRetryAssetLoader::LoadAsset", this, runTask, evaluationFunc, onSuccess, onFailure, MaxRetries, StartBackoffTimeoutMilliseconds / 1000f);
+ }
+
+ public void CancelAllLoads()
+ {
+ assetLoader.CancelAllLoads();
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/ExponentialBackoffRetryAssetLoader.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/ExponentialBackoffRetryAssetLoader.cs.meta
new file mode 100644
index 0000000..662fd11
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/ExponentialBackoffRetryAssetLoader.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: c35f15cdd70d86e4e925b26bc1b6c8bc
+timeCreated: 1455725219
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/FilePersistenceStrategy.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/FilePersistenceStrategy.cs
new file mode 100644
index 0000000..c0e4deb
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/FilePersistenceStrategy.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System.IO;
+
+namespace Improbable.Assets
+{
+ public class FilePersistenceStrategy : MachineCache.IPersistenceStrategy
+ {
+ public void WriteToCacheFile(string outputCacheFile, byte[] resource)
+ {
+ File.WriteAllBytes(outputCacheFile, resource);
+ }
+
+ public byte[] ReadFromCacheFile(string inputCacheFile)
+ {
+ return File.ReadAllBytes(inputCacheFile);
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/FilePersistenceStrategy.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/FilePersistenceStrategy.cs.meta
new file mode 100644
index 0000000..f4b3e70
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/FilePersistenceStrategy.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 6124c94b427701f4fa3c0b6912bf798e
+timeCreated: 1455879378
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/GameObjectFromAssetBundleLoader.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/GameObjectFromAssetBundleLoader.cs
new file mode 100644
index 0000000..ccadc3e
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/GameObjectFromAssetBundleLoader.cs
@@ -0,0 +1,124 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using Improbable.Assets;
+using UnityEngine;
+
+namespace Improbable.Unity.Assets
+{
+ class GameObjectFromAssetBundleLoader : IAssetLoader
+ {
+ private readonly IAssetLoader assetBundleLoader;
+ private HashSet inFlightAssetRequests = new HashSet();
+
+ //Each call to LoadAsset will have it's Actions stored in an OnLoadCallback object and called
+ //later, once the asset bundle has been loaded.
+ private struct OnLoadCallback
+ {
+ public Action onSuccess;
+ public Action onFail;
+ }
+
+ //Stores a look up table of prefab names, to a list of call backs that are stored per call to LoadAsset
+ private Dictionary> mapPrefabToLoadCallbacks = new Dictionary>();
+
+ public GameObjectFromAssetBundleLoader(IAssetLoader assetBundleLoader)
+ {
+ this.assetBundleLoader = assetBundleLoader;
+ }
+
+ public int NumberOfInFlightAssetRequests()
+ {
+ return inFlightAssetRequests.Count;
+ }
+
+ public void LoadAsset(string prefabName, Action onGameObjectLoaded, Action onError)
+ {
+ var callBackPair = new OnLoadCallback { onSuccess = onGameObjectLoaded, onFail = onError };
+ if (!mapPrefabToLoadCallbacks.ContainsKey(prefabName))
+ {
+ mapPrefabToLoadCallbacks.Add(prefabName, new List { callBackPair });
+ }
+ else
+ {
+ mapPrefabToLoadCallbacks[prefabName].Add(callBackPair);
+ }
+
+ if (!inFlightAssetRequests.Contains(prefabName))
+ {
+ inFlightAssetRequests.Add(prefabName);
+ assetBundleLoader.LoadAsset(prefabName,
+ loadedAssetBundle => OnAssetBundleLoaded(loadedAssetBundle, prefabName),
+ ex => OnAssetLoadFailure(prefabName, ex));
+ }
+ }
+
+ public void CancelAllLoads()
+ {
+ assetBundleLoader.CancelAllLoads();
+
+ mapPrefabToLoadCallbacks.Clear();
+
+ inFlightAssetRequests.Clear();
+ }
+
+ private void OnAssetBundleLoaded(AssetBundle loadedAssetBundle, string prefabName)
+ {
+ inFlightAssetRequests.Remove(prefabName);
+
+ List listOfCallbacks;
+ if (prefabName != null && mapPrefabToLoadCallbacks.TryGetValue(prefabName, out listOfCallbacks))
+ {
+ for (var i = 0; i < listOfCallbacks.Count; ++i)
+ {
+ try
+ {
+ var gameObject = loadedAssetBundle.LoadAsset(prefabName);
+ if (gameObject == null)
+ {
+ listOfCallbacks[i].onFail(new Exception(string.Format("Could not load the game object from asset '{0}'.", prefabName)));
+ }
+ else
+ {
+ listOfCallbacks[i].onSuccess(gameObject);
+ }
+ }
+ catch (Exception ex)
+ {
+ listOfCallbacks[i].onFail(ex);
+ }
+ }
+
+ mapPrefabToLoadCallbacks.Remove(prefabName);
+ }
+ else
+ {
+ Debug.LogErrorFormat("Prefab {0} not found in asset load success callback", prefabName);
+ }
+
+ //Always unload the asset bundle regardless
+ loadedAssetBundle.Unload(unloadAllLoadedObjects: false);
+ }
+
+ private void OnAssetLoadFailure(string prefabName, Exception ex)
+ {
+ inFlightAssetRequests.Remove(prefabName);
+
+ List listOfCallbacks;
+ if (mapPrefabToLoadCallbacks.TryGetValue(prefabName, out listOfCallbacks))
+ {
+ for (var i = 0; i < listOfCallbacks.Count; ++i)
+ {
+ listOfCallbacks[i].onFail(ex);
+ }
+
+ mapPrefabToLoadCallbacks.Remove(prefabName);
+ }
+ else
+ {
+ Debug.LogErrorFormat("Prefab {0} not found in asset load fail callback", prefabName);
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/GameObjectFromAssetBundleLoader.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/GameObjectFromAssetBundleLoader.cs.meta
new file mode 100644
index 0000000..edc4f44
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/GameObjectFromAssetBundleLoader.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 2df7a964d785f6e4fbc5ca00b2162274
+timeCreated: 1444833357
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetDatabase.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetDatabase.cs
new file mode 100644
index 0000000..b2e6b21
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetDatabase.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+namespace Improbable.Assets
+{
+ ///
+ /// Maintains a cache of assets
+ ///
+ public interface IAssetDatabase : IAssetLoader
+ {
+ ///
+ /// Returns true and sets the prefab game object output parameter to a valid instance if the prefab has been
+ /// successfully loaded via .
+ /// If the prefab has not been loaded yet, then this method returns false and sets the prefab game object output
+ /// parameter to null.
+ ///
+ /// the name of the prefab to obtain
+ /// the prefab game object
+ /// true if successful, otherwise false
+ ///
+ /// Subsequent calls to this method will return the same prefab game object instance (due to caching).
+ /// You must clone the returned prefab game object to make your own instance.
+ ///
+ bool TryGet(string prefabName, out TGameObject prefabGameObject);
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetDatabase.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetDatabase.cs.meta
new file mode 100644
index 0000000..3f2a878
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetDatabase.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: cf79adea61d801943be0734f7a78b68c
+timeCreated: 1455879378
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetLoader.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetLoader.cs
new file mode 100644
index 0000000..582284a
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetLoader.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+
+namespace Improbable.Assets
+{
+ public interface IAssetLoader
+ {
+ ///
+ /// Downloads an asset and invokes the callback when succeeded.
+ ///
+ /// the name of the prefab for which to load the asset.
+ /// called after the asset has been loaded. This callback is called in the calling thread.
+ /// called if for any reason the asset loading failed. This callback is called in the calling thread.
+ ///
+ /// This method is not blocking.
+ ///
+ void LoadAsset(string prefabName, Action onAssetLoaded, Action onError);
+
+ ///
+ /// Cancels all ongoing asset loading requests.
+ ///
+ void CancelAllLoads();
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetLoader.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetLoader.cs.meta
new file mode 100644
index 0000000..507ec25
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IAssetLoader.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5c58cfe5fec60e24db067a8fc407b3d5
+timeCreated: 1455879378
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IMachineCache.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IMachineCache.cs
new file mode 100644
index 0000000..66b69e4
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IMachineCache.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+namespace Improbable.Assets
+{
+ ///
+ /// Unit test wrapper for MachineCache
+ /// ///
+ ///
+ interface IMachineCache
+ {
+ bool TryAdd(string key, TIn cacheItem);
+ bool TryAddOrUpdate(string key, TIn cacheItem);
+ bool TryGet(string key, out TOut result);
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IMachineCache.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IMachineCache.cs.meta
new file mode 100644
index 0000000..f5843c7
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/IMachineCache.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ba3ab89976f1bf04088338a507fb19c9
+timeCreated: 1499176666
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/LocalAssetBundleLoader.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/LocalAssetBundleLoader.cs
new file mode 100644
index 0000000..497ea59
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/LocalAssetBundleLoader.cs
@@ -0,0 +1,63 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.IO;
+using Improbable.Assets;
+using UnityEngine;
+
+namespace Improbable.Unity.Assets
+{
+ ///
+ /// A filesystem-based asset bundle loader.
+ /// This implementation loads unity3d files from assets stored in the file strucuture required by the asset database.
+ ///
+ class LocalAssetBundleLoader : IAssetLoader
+ {
+ private const string entityPrefabSubdir = "unity";
+
+ private readonly string entityPrefabsPath;
+
+ /// The directory where to find asset bundles.
+ public LocalAssetBundleLoader(string assetBundlesDir)
+ {
+ entityPrefabsPath = Path.Combine(assetBundlesDir, entityPrefabSubdir);
+ }
+
+ public void LoadAsset(string prefabName, Action onAssetLoaded, Action onError)
+ {
+ var assetBundlePath = Path.Combine(entityPrefabsPath, Platform.PrefabNameToAssetBundleName(prefabName.ToLower()));
+
+ try
+ {
+ if (!File.Exists(assetBundlePath))
+ {
+ throw new IOException(string.Format("Failed to load prefab's '{0}' asset bundle from file '{1}'.\n", prefabName, assetBundlePath)
+ + "Asset is either missing or the local asset bundle path is incorrect.");
+ }
+
+ var assetBundle = CreateAssetBundleFromFile(assetBundlePath);
+ if (assetBundle == null)
+ {
+ throw new Exception(string.Format("Failed to load prefab's '{0}' asset bundle from file '{1}'.\n", prefabName, assetBundlePath)
+ + "Asset is most likely corrupted.");
+ }
+
+ onAssetLoaded(assetBundle);
+ }
+ catch (Exception e)
+ {
+ onError(e);
+ }
+ }
+
+ public void CancelAllLoads()
+ {
+ // LoadAsset is instantaneous, so no need to do anything here.
+ }
+
+ private static AssetBundle CreateAssetBundleFromFile(string assetBundlePath)
+ {
+ return AssetBundle.LoadFromFile(assetBundlePath);
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/LocalAssetBundleLoader.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/LocalAssetBundleLoader.cs.meta
new file mode 100644
index 0000000..27f6505
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/LocalAssetBundleLoader.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: a786f8fde2c7b0b47b96966044de60d2
+timeCreated: 1444833361
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/MachineCache.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/MachineCache.cs
new file mode 100644
index 0000000..905f9d0
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/MachineCache.cs
@@ -0,0 +1,227 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.IO;
+using System.Text;
+using System.Threading;
+using UnityEngine;
+
+namespace Improbable.Assets
+{
+ ///
+ /// A filesystem-based cache. It imports resources of one type and returns them in another.
+ /// This implementation attempts to use atomic file operations to ensure consistency.
+ ///
+ /// the type of resource to be stored in the cache (stored as a file).
+ /// the type of resource to be retrieved from the cache (mapped from the cached resource file).
+ ///
+ /// The implementation is heavily inspired by Nuget's repository cache which can be found here:
+ /// https://github.com/Haacked/NuGet/blob/9f25709fafb0d8e8fc2a3b34c1b77a1cb4b8a539/src/Core/Repositories/MachineCache.cs
+ ///
+ public class MachineCache : IMachineCache
+ {
+ private static readonly TimeSpan MutexWaitTime = TimeSpan.FromMinutes(3);
+ private readonly IPersistenceStrategy persistenceStrategy;
+
+ private string CachePath { get; set; }
+
+ public MachineCache(string cachePath, IPersistenceStrategy persistenceStrategy)
+ {
+ this.persistenceStrategy = persistenceStrategy;
+ CachePath = cachePath;
+ CreateCache();
+ }
+
+ ///
+ /// Attempt to Add an item to the cache. Will not update existing entries.
+ ///
+ /// the key used for later retrieval
+ /// the item to be cached
+ /// true if successful otherwise false
+ public bool TryAdd(string key, TIn cacheItem)
+ {
+ var path = GetFilePath(key);
+ if (!File.Exists(path))
+ {
+ AtomicWriteToCache(cacheItem, path);
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Attempt to Add an item to the cache. Will update existing entries.
+ ///
+ /// the key used for later retrieval
+ /// the item to be cached
+ /// true if successful otherwise false
+ public bool TryAddOrUpdate(string key, TIn cacheItem)
+ {
+ try
+ {
+ var path = GetFilePath(key);
+ AtomicWriteToCache(cacheItem, path);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogErrorFormat("Attempt to add or update an item to the cache failed.\n{0} = {1}\n{2}", key, cacheItem, ex);
+ return false;
+ }
+ }
+
+ ///
+ /// Attempt to get an item from the cache
+ ///
+ /// the key of the item to retrieve
+ /// the item from the cache
+ /// true if successful otherwise false
+ public bool TryGet(string key, out TOut result)
+ {
+ try
+ {
+ var path = GetFilePath(key);
+ result = persistenceStrategy.ReadFromCacheFile(path);
+ return true;
+ }
+ catch (Exception)
+ {
+ result = default(TOut);
+ return false;
+ }
+ }
+
+ private string GetFilePath(string key)
+ {
+ return Path.Combine(CachePath, Uri.EscapeDataString(key));
+ }
+
+ private void CreateCache()
+ {
+ if (!Directory.Exists(CachePath))
+ {
+ Directory.CreateDirectory(CachePath);
+ }
+ }
+
+ private void AtomicWriteToCache(TIn cacheItem, string path)
+ {
+ var tmp = Path.GetTempFileName();
+ persistenceStrategy.WriteToCacheFile(tmp, cacheItem);
+ TryAct(() =>
+ {
+ if (!File.Exists(path))
+ {
+ File.Move(tmp, path);
+ }
+ else
+ {
+ var backup = Path.GetTempFileName();
+ File.Replace(tmp, path, backup);
+ File.Delete(backup);
+ }
+
+ return true;
+ }, path);
+ }
+
+ private static string GenerateUniqueToken(string caseInsensitiveKey)
+ {
+ // We need something that is stable across all platforms and processes.
+ // Use an adaption of the mscorlib's string hash.
+ // http://referencesource.microsoft.com/#mscorlib/system/string.cs,827
+ var pathBytes = Encoding.UTF8.GetBytes(caseInsensitiveKey.ToUpperInvariant());
+
+ // Disable overflow checking, we're not doing "real" math here.
+ unchecked
+ {
+ int hash1 = 5381;
+ int hash2 = hash1;
+
+ for (int i = 0; i < pathBytes.Length; i++)
+ {
+ var c = pathBytes[i];
+ hash1 = ((hash1 << 5) + hash1) ^ c;
+ if (i == pathBytes.Length - 1 || pathBytes[i + 1] == '\0')
+ {
+ break;
+ }
+
+ hash2 = ((hash2 << 5) + hash2) ^ pathBytes[i + 1];
+ }
+
+ return (hash1 + (hash2 * 1566083941)).ToString();
+ }
+ }
+
+ ///
+ /// We use this method instead of the "safe" methods in FileSystem because it attempts to retry multiple times with
+ /// delays.
+ /// In our case, if we are unable to perform IO over the machine cache, we want to quit trying immediately.
+ ///
+ private static void TryAct(Func action, string path)
+ {
+ try
+ {
+ // Global: machine cache is per user across TS sessions
+ var mutexName = "Global\\" + GenerateUniqueToken(Path.GetFullPath(path));
+ using (var mutex = new Mutex(false, mutexName))
+ {
+ bool owner = false;
+ try
+ {
+ try
+ {
+ owner = mutex.WaitOne(MutexWaitTime);
+ // ideally we should throw an exception here if !owner such as
+ // throw new TimeoutException(string.Format("Timeout waiting for Machine Cache mutex for {0}", fullPath));
+ // we decided against it: machine cache operations being "best effort" basis.
+ // this may cause "File in use" exceptions for long lasting operations such as downloading a large package on
+ // a slow network connection
+ }
+ catch (AbandonedMutexException)
+ {
+ // TODO: consider logging a warning; abandoning a mutex is an indication something wrong is going on
+ owner = true; // now mine
+ }
+
+ action();
+ }
+ finally
+ {
+ if (owner)
+ {
+ mutex.ReleaseMutex();
+ }
+ }
+ }
+ }
+ catch (UnauthorizedAccessException)
+ {
+ // Do nothing if this fails.
+ }
+ }
+
+ ///
+ /// The strategy that describes how the cache item is persisted and retrieved from disk
+ ///
+ public interface IPersistenceStrategy
+ {
+ ///
+ /// Called when the cache is persisting the item to disk
+ ///
+ /// the file to write to
+ ///
+ /// Note: outputCacheFile may be a temporary file and should not be used later for any reason.
+ void WriteToCacheFile(string outputCacheFile, TIn resource);
+
+ ///
+ /// Load and deserialize a cache item
+ ///
+ /// the filename to read from
+ /// The cache item
+ TOut ReadFromCacheFile(string inputCacheFile);
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/MachineCache.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/MachineCache.cs.meta
new file mode 100644
index 0000000..eaa9830
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/MachineCache.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 9e63d197aee438a46a68b2418e673945
+timeCreated: 1455879378
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/Platform.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/Platform.cs
new file mode 100644
index 0000000..2fde9b2
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/Platform.cs
@@ -0,0 +1,94 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Improbable.Assets
+{
+ ///
+ /// Platform provides methods to get prefab asset bundle suffixes for different platforms and build targets.
+ ///
+ public static class Platform
+ {
+ private static readonly string windowsAssetBundleSuffix = "windows";
+ private static readonly string osxAssetBundleSuffix = "osx";
+ private static readonly string iosAssetBundleSuffix = "ios";
+ private static readonly string linuxAssetBundleSuffix = "linux";
+ private static readonly string androidAssetBundleSuffix = "android";
+
+ private static readonly Dictionary runtimePlatformToAssetBundleSuffix = new Dictionary
+ {
+ { RuntimePlatform.WindowsEditor, windowsAssetBundleSuffix },
+ { RuntimePlatform.WindowsPlayer, windowsAssetBundleSuffix },
+
+ { RuntimePlatform.OSXEditor, osxAssetBundleSuffix },
+ { RuntimePlatform.OSXPlayer, osxAssetBundleSuffix },
+
+ { RuntimePlatform.LinuxPlayer, linuxAssetBundleSuffix },
+ { RuntimePlatform.LinuxEditor, linuxAssetBundleSuffix },
+
+ { RuntimePlatform.IPhonePlayer, iosAssetBundleSuffix },
+
+ { RuntimePlatform.Android, androidAssetBundleSuffix }
+ };
+
+ ///
+ /// BuildPlatform is used instead of BuildTarget (which is defined only in UnityEditor).
+ ///
+ public enum BuildPlatform
+ {
+ Windows,
+ OSX,
+ Linux,
+ iOS,
+ Android
+ }
+
+ private static readonly Dictionary buildPlatformToAssetBundleSuffix = new Dictionary
+
+ {
+ { BuildPlatform.Windows, windowsAssetBundleSuffix },
+ { BuildPlatform.OSX, osxAssetBundleSuffix },
+ { BuildPlatform.Linux, linuxAssetBundleSuffix },
+ { BuildPlatform.iOS, iosAssetBundleSuffix },
+ { BuildPlatform.Android, androidAssetBundleSuffix },
+ };
+
+ ///
+ /// Returns the asset bundle suffix for the given platform.
+ ///
+ public static string RuntimePlatformToAssetBundleSuffix(RuntimePlatform platform)
+ {
+ string suffix;
+ if (!runtimePlatformToAssetBundleSuffix.TryGetValue(platform, out suffix))
+ {
+ throw new ArgumentException("No asset bundle name suffix exists for that platform.");
+ }
+
+ return suffix;
+ }
+
+ ///
+ /// Returns the asset bundle suffix for the given build target.
+ ///
+ public static string BuildPlatformToAssetBundleSuffix(BuildPlatform buildPlatform)
+ {
+ string suffix;
+ if (!buildPlatformToAssetBundleSuffix.TryGetValue(buildPlatform, out suffix))
+ {
+ throw new ArgumentException("No asset bundle name suffix exists for that build platform.");
+ }
+
+ return suffix;
+ }
+
+ ///
+ /// Gets the asset bundle name for the given prefab name, by appending the correct suffix for the platform.
+ ///
+ public static string PrefabNameToAssetBundleName(string prefabName)
+ {
+ return String.Format("{0}@{1}", prefabName, RuntimePlatformToAssetBundleSuffix(Application.platform));
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/Platform.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/Platform.cs.meta
new file mode 100644
index 0000000..562ca22
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/Platform.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 92ae7b4177ea7f342804c777d8a5e37f
+timeCreated: 1480436804
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabCompiler.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabCompiler.cs
new file mode 100644
index 0000000..a57f03c
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabCompiler.cs
@@ -0,0 +1,101 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+
+using Improbable.Unity.Export;
+using Improbable.Unity.Visualizer;
+using UnityEngine;
+
+namespace Improbable.Unity.Assets
+{
+ public class PrefabCompiler
+ {
+ private readonly BehaviourWorkerCompatibilityCache compatibilityCache;
+ private readonly WorkerPlatform workerPlatform;
+
+ public PrefabCompiler(WorkerPlatform workerPlatform)
+ {
+ this.workerPlatform = workerPlatform;
+ compatibilityCache = new BehaviourWorkerCompatibilityCache(workerPlatform);
+ }
+
+ public void Compile(GameObject prefab)
+ {
+ CompileRecursively(prefab);
+ }
+
+ private void CompileRecursively(GameObject prefab)
+ {
+ InvokePrefabExportProcessors(prefab);
+ DisableVisualizers(prefab);
+ DisableWrongPlatformMonoBehaviours(prefab);
+ ExportProcessChildren(prefab);
+ }
+
+ private void DisableWrongPlatformMonoBehaviours(GameObject prefab)
+ {
+ var components = prefab.GetComponents();
+ for (int i = 0; i < components.Length; i++)
+ {
+ if (components[i] != null && !compatibilityCache.IsCompatibleBehaviour(components[i].GetType()))
+ {
+ GameObject.DestroyImmediate(components[i], true);
+ }
+ }
+ }
+
+ private void InvokePrefabExportProcessors(GameObject prefab)
+ {
+ var components = prefab.GetComponents();
+
+ foreach (var component in components)
+ {
+ if (component is IPrefabExportProcessor)
+ {
+ var processor = component as IPrefabExportProcessor;
+ processor.ExportProcess(workerPlatform);
+
+ if (ShouldRemoveFromPrefab(processor))
+ {
+ Object.DestroyImmediate(component, true);
+ }
+ }
+ }
+ }
+
+ private static bool ShouldRemoveFromPrefab(object exportProcessor)
+ {
+ var attributes = exportProcessor.GetType().GetCustomAttributes(typeof(KeepOnExportedPrefabAttribute), true);
+ return attributes.Length == 0;
+ }
+
+ private static void DisableVisualizers(GameObject prefab)
+ {
+ var components = prefab.GetComponents();
+
+ foreach (var component in components)
+ {
+ if (component == null)
+ {
+ continue;
+ }
+
+ if (VisualizerMetadataLookup.Instance.IsVisualizer(component.GetType()))
+ {
+ component.enabled = false;
+ }
+ }
+ }
+
+ private void ExportProcessChildren(GameObject prefab)
+ {
+ var prefabTransform = prefab.transform;
+ if (prefabTransform.childCount > 0)
+ {
+ foreach (Transform childTransform in prefabTransform)
+ {
+ CompileRecursively(childTransform.gameObject);
+ }
+ }
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabCompiler.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabCompiler.cs.meta
new file mode 100644
index 0000000..918f2eb
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabCompiler.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 9c5ad41057599e34ca564d5563d304ea
+timeCreated: 1448883131
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabGameObjectLoader.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabGameObjectLoader.cs
new file mode 100644
index 0000000..555b7eb
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabGameObjectLoader.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using System.Linq;
+using Improbable.Assets;
+using UnityEngine;
+
+namespace Improbable.Unity.Assets
+{
+ public class PrefabGameObjectLoader : IAssetLoader
+ {
+ private const string AssetDatabaseTypeName = "Improbable.Unity.EditorTools.Assets.EditorPrefabGameObjectLoader";
+ private IAssetLoader gameObjectLoader;
+
+ public PrefabGameObjectLoader()
+ {
+ CreateEditorPrefabAssetDatabase();
+ }
+
+ private void CreateEditorPrefabAssetDatabase()
+ {
+ Type assetDatabaseType = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
+ from type in assembly.GetTypes()
+ where type.FullName == AssetDatabaseTypeName
+ select type).First();
+
+ if (assetDatabaseType == null)
+ {
+ throw new Exception(String.Format("Could not find required asset database type {0}", AssetDatabaseTypeName));
+ }
+
+ gameObjectLoader = Activator.CreateInstance(assetDatabaseType) as IAssetLoader;
+ }
+
+ public void LoadAsset(string prefabName, Action onGameObjectLoaded, Action onError)
+ {
+ gameObjectLoader.LoadAsset(prefabName, onGameObjectLoaded, onError);
+ }
+
+ public void CancelAllLoads()
+ {
+ gameObjectLoader.CancelAllLoads();
+ }
+ }
+}
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabGameObjectLoader.cs.meta b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabGameObjectLoader.cs.meta
new file mode 100644
index 0000000..6e7cd2c
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PrefabGameObjectLoader.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 501c1eb1107d62a4cb285eebca4107da
+timeCreated: 1444833358
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PreprocessingGameObjectLoader.cs b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PreprocessingGameObjectLoader.cs
new file mode 100644
index 0000000..59cb641
--- /dev/null
+++ b/workers/unity/Assets/Plugins/Improbable/Sdk/Src/Unity/Assets/PreprocessingGameObjectLoader.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Improbable Worlds Ltd, All Rights Reserved
+
+using System;
+using Improbable.Assets;
+using Improbable.Unity.Core;
+using UnityEngine;
+
+namespace Improbable.Unity.Assets
+{
+ public class PreprocessingGameObjectLoader : IAssetLoader
+ {
+ private readonly IAssetLoader gameObjectLoader;
+ private readonly PrefabCompiler prefabCompiler;
+
+ public PreprocessingGameObjectLoader(IAssetLoader gameObjectLoader)
+ {
+ this.gameObjectLoader = gameObjectLoader;
+ this.prefabCompiler = new PrefabCompiler(SpatialOS.Configuration.WorkerPlatform);
+ }
+
+ public void LoadAsset(string prefabName, Action