void OnEnable()
        {
            encryptionKeysEnabled     = UMAABMSettings.GetEncryptionEnabled();
            currentEncryptionPassword = newEncryptionPassword = UMAABMSettings.GetEncryptionPassword();
            currentEncryptionSuffix   = newEncryptionSuffix = UMAABMSettings.GetEncryptionSuffix();
            if (currentEncryptionSuffix == "")
            {
                currentEncryptionSuffix = newEncryptionSuffix = DEFAULT_ENCRYPTION_SUFFIX;
            }
            currentEncodeNamesSetting = newEncodeNamesSetting = UMAABMSettings.GetEncodeNames();
#if ENABLE_IOS_APP_SLICING
            currentAppSlicingSetting = UMAABMSettings.GetBuildForSlicing();
#endif
            _enableBundleIndexVersioning = UMAABMSettings.EnableBundleIndexVersioning;
            //localAssetBundleServer status
            _enableLocalAssetBundleServer = EditorPrefs.GetBool(Application.dataPath + "LocalAssetBundleServerEnabled");
            _port = EditorPrefs.GetInt(Application.dataPath + "LocalAssetBundleServerPort", 7888);
            //When the window is opened we still need to tell the user if the port is available so
            if (!_enableLocalAssetBundleServer)
            {
                UpdateServer(true);
                ServerStop();
                if (serverException)
                {
                    portError = true;
                }
            }
            else
            {
                UpdateServer();
            }
        }
Example #2
0
        public static byte[] Encrypt(byte[] value, ref byte[] IVout)
        {
            var pass = UMAABMSettings.GetEncryptionPassword();

            if (String.IsNullOrEmpty(pass))
            {
                throw new Exception("[EncryptUtil] could not perform any encryption because not encryption password was set in UMAAssetBundleManager.");
            }

            IVout = GenerateIV(pass);

            return(Encrypt(BuildKey(pass, IVout), value));
        }
        void OnFocus()
        {
            encryptionKeysEnabled     = UMAABMSettings.GetEncryptionEnabled();
            currentEncryptionPassword = newEncryptionPassword = UMAABMSettings.GetEncryptionPassword();
            currentEncryptionSuffix   = newEncryptionSuffix = UMAABMSettings.GetEncryptionSuffix();
            if (currentEncryptionSuffix == "")
            {
                currentEncryptionSuffix = newEncryptionSuffix = DEFAULT_ENCRYPTION_SUFFIX;
            }
            currentEncodeNamesSetting = newEncodeNamesSetting = UMAABMSettings.GetEncodeNames();
#if ENABLE_IOS_APP_SLICING
            currentAppSlicingSetting = UMAABMSettings.GetBuildForSlicing();
#endif
        }
Example #4
0
        public static void BuildAssetBundles()
        {
            var thisIndexAssetPath      = "";
            var thisEncryptionAssetPath = "";

            try {
                // Choose the output path according to the build target.
                string outputPath = CreateAssetBundleDirectory();

                var options = BuildAssetBundleOptions.None;

                bool shouldCheckODR = EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS;
#if UNITY_TVOS
                shouldCheckODR |= EditorUserBuildSettings.activeBuildTarget == BuildTarget.tvOS;
#endif
                if (shouldCheckODR)
                {
#if ENABLE_IOS_ON_DEMAND_RESOURCES
                    if (PlayerSettings.iOS.useOnDemandResources)
                    {
                        options |= BuildAssetBundleOptions.UncompressedAssetBundle;
                    }
                    else if (UMAABMSettings.GetEncryptionEnabled())
                    {
                        options |= BuildAssetBundleOptions.ChunkBasedCompression;
                    }
#endif
#if ENABLE_IOS_APP_SLICING
                    if (UMAABMSettings.GetBuildForSlicing())
                    {
                        options |= BuildAssetBundleOptions.UncompressedAssetBundle;
                    }
                    else if (!PlayerSettings.iOS.useOnDemandResources && UMAABMSettings.GetEncryptionEnabled())
                    {
                        options |= BuildAssetBundleOptions.ChunkBasedCompression;
                    }
#endif
                }
                if (UMAABMSettings.GetEncryptionEnabled())
                {
                    if (!shouldCheckODR)
                    {
                        options |= BuildAssetBundleOptions.ChunkBasedCompression;
                    }
                    options |= BuildAssetBundleOptions.ForceRebuildAssetBundle;
                }

                //AssetBundleIndex
                AssetBundleIndex thisIndex = ScriptableObject.CreateInstance <AssetBundleIndex>();

                string[] assetBundleNamesArray = AssetDatabase.GetAllAssetBundleNames();

                //Generate a buildmap as we go
                AssetBundleBuild[] buildMap = new AssetBundleBuild[assetBundleNamesArray.Length + 1];                //+1 for the index bundle
                for (int i = 0; i < assetBundleNamesArray.Length; i++)
                {
                    string bundleName = assetBundleNamesArray[i];

                    string[] assetBundleAssetsArray = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName);
                    //If there are no assets added to this bundle show a warning telling the user to remove Unused asset bundle names
                    if (assetBundleAssetsArray == null || assetBundleAssetsArray.Length == 0)
                    {
                        Debug.Log(assetBundleNamesArray[i] + " was an empty assetBundle. Please do 'Remove Unused Names' in the 'Asset Labels' section of the Inspector");
                    }

                    thisIndex.bundlesIndex.Add(new AssetBundleIndex.AssetBundleIndexList(bundleName));

                    if (bundleName.IndexOf('.') > -1)
                    {
                        buildMap[i].assetBundleName    = bundleName.Split('.')[0];
                        buildMap[i].assetBundleVariant = bundleName.Split('.')[1];
                    }
                    else
                    {
                        buildMap[i].assetBundleName = bundleName;
                    }

                    buildMap[i].assetNames = assetBundleAssetsArray;

                    foreach (string path in assetBundleAssetsArray)
                    {
                        var sysPath  = Path.Combine(Application.dataPath, path);
                        var filename = Path.GetFileNameWithoutExtension(sysPath);
                        var tempObj  = AssetDatabase.LoadMainAssetAtPath(path);
                        thisIndex.bundlesIndex[i].AddItem(filename, tempObj);
                    }
                }

                thisIndexAssetPath = "Assets/" + Utility.GetPlatformName() + "Index.asset";
                thisIndex.name     = "AssetBundleIndex";
                AssetDatabase.CreateAsset(thisIndex, thisIndexAssetPath);
                AssetImporter thisIndexAsset = AssetImporter.GetAtPath(thisIndexAssetPath);
                thisIndexAsset.assetBundleName = Utility.GetPlatformName() + "index";
                buildMap[assetBundleNamesArray.Length].assetBundleName = Utility.GetPlatformName() + "index";
                buildMap[assetBundleNamesArray.Length].assetNames      = new string[1] {
                    "Assets/" + Utility.GetPlatformName() + "Index.asset"
                };

                //Build the current state so we can get the AssetBundleManifest object and add its values to OUR index
                var assetBundleManifest = BuildPipeline.BuildAssetBundles(outputPath, buildMap, options, EditorUserBuildSettings.activeBuildTarget);
                if (assetBundleManifest == null)
                {
                    throw new System.Exception("Your assetBundles did not build properly.");
                }
                //reload the saved index (TODO may not be necessary)
                thisIndex = AssetDatabase.LoadAssetAtPath <AssetBundleIndex>("Assets/" + Utility.GetPlatformName() + "Index.asset");
                //Get any bundles with variants
                string[] bundlesWithVariant = assetBundleManifest.GetAllAssetBundlesWithVariant();
                thisIndex.bundlesWithVariant = bundlesWithVariant;
                //then loop over each bundle in the bundle names and get the bundle specific data
                for (int i = 0; i < assetBundleNamesArray.Length; i++)
                {
                    string[] assetBundleAssetsArray = AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleNamesArray[i]);
                    //If there are no assets added to this bundle continue because it wont be in the resulting assetBundleManifest
                    if (assetBundleAssetsArray == null)
                    {
                        continue;
                    }
                    string   assetBundleHash    = assetBundleManifest.GetAssetBundleHash(assetBundleNamesArray[i]).ToString();
                    string[] allDependencies    = assetBundleManifest.GetAllDependencies(assetBundleNamesArray[i]);
                    string[] directDependencies = assetBundleManifest.GetDirectDependencies(assetBundleNamesArray[i]);
                    thisIndex.bundlesIndex[i].assetBundleHash    = assetBundleHash;
                    thisIndex.bundlesIndex[i].allDependencies    = allDependencies;
                    thisIndex.bundlesIndex[i].directDependencies = directDependencies;
                    //Add suffixed names to the index if enabled and we are using encrption
                    //we cant append the suffix to the index file because when we are loading we dont know the set suffix or suffixed names until we have the index
                    //it also cant be the same as the unencrypted name because it needs a different memory address, so its going to have to be name + "encrypted"
                    if (UMAABMSettings.GetEncryptionEnabled())
                    {
                        var encryptedBundleName = assetBundleNamesArray[i] == Utility.GetPlatformName() + "index" ? assetBundleNamesArray[i] + "encrypted" : assetBundleNamesArray[i] + UMAABMSettings.GetEncryptionSuffix();
                        if (UMAABMSettings.GetEncodeNames() && assetBundleNamesArray[i] != Utility.GetPlatformName() + "index")
                        {
                            encryptedBundleName = EncryptionUtil.EncodeFileName(encryptedBundleName);
                        }
                        thisIndex.bundlesIndex[i].encryptedName = encryptedBundleName;
                    }
                }
                var relativeAssetBundlesOutputPathForPlatform = Path.Combine(Utility.AssetBundlesOutputPath, Utility.GetPlatformName());
                //Update and Save the index asset and build again. This will store the updated asset in the windowsindex asset bundle
                EditorUtility.SetDirty(thisIndex);
                AssetDatabase.SaveAssets();
                //Build the Index AssetBundle
                var indexBuildMap = new AssetBundleBuild[1];
                indexBuildMap[0] = buildMap[assetBundleNamesArray.Length];
                BuildPipeline.BuildAssetBundles(outputPath, indexBuildMap, options, EditorUserBuildSettings.activeBuildTarget);

                //Save a json version of the data- this can be used for uploading to a server to update a database or something
                string thisIndexJson     = JsonUtility.ToJson(thisIndex);
                var    thisIndexJsonPath = Path.Combine(relativeAssetBundlesOutputPathForPlatform, Utility.GetPlatformName().ToLower()) + "index.json";
                File.WriteAllText(thisIndexJsonPath, thisIndexJson);
                //Build Encrypted Bundles
                if (UMAABMSettings.GetEncryptionEnabled())
                {
                    var encryptedBuildMap = new AssetBundleBuild[1];
                    var EncryptionAsset   = ScriptableObject.CreateInstance <UMAEncryptedBundle>();
                    EncryptionAsset.name    = "EncryptedData";
                    thisEncryptionAssetPath = "Assets/EncryptedData.asset";
                    AssetDatabase.CreateAsset(EncryptionAsset, thisEncryptionAssetPath);
                    //var encryptedOutputPath = Path.Combine(outputPath, "Encrypted");
                    var encryptedOutputPath = Path.Combine(Utility.AssetBundlesOutputPath, Path.Combine("Encrypted", Utility.GetPlatformName()));
                    if (!Directory.Exists(encryptedOutputPath))
                    {
                        Directory.CreateDirectory(encryptedOutputPath);
                    }
                    for (int bmi = 0; bmi < buildMap.Length; bmi++)                    //-1 to not include the index bundle (or maybe we do encrypt the index bundle?)
                    {
                        var thisEncryptionAsset = AssetDatabase.LoadAssetAtPath <UMAEncryptedBundle>(thisEncryptionAssetPath);
                        //get the data from the unencrypted bundle and encrypt it into the EncryptedData asset
                        thisEncryptionAsset.GenerateData(buildMap[bmi].assetBundleName, Path.Combine(relativeAssetBundlesOutputPathForPlatform, buildMap[bmi].assetBundleName));
                        EditorUtility.SetDirty(thisEncryptionAsset);
                        AssetDatabase.SaveAssets();
                        //Sort out the name of this encrypted bundle
                        var encryptedBundleName = "";
                        if (buildMap[bmi].assetBundleName != Utility.GetPlatformName() + "index")
                        {
                            encryptedBundleName = buildMap[bmi].assetBundleName + UMAABMSettings.GetEncryptionSuffix();
                            if (UMAABMSettings.GetEncodeNames() && buildMap[bmi].assetBundleName != Utility.GetPlatformName() + "index")
                            {
                                encryptedBundleName = EncryptionUtil.EncodeFileName(encryptedBundleName);
                            }
                        }
                        else
                        {
                            encryptedBundleName = buildMap[bmi].assetBundleName + "encrypted";
                        }
                        //set the Build map value
                        encryptedBuildMap[0].assetBundleName = encryptedBundleName;
                        encryptedBuildMap[0].assetNames      = new string[1] {
                            "Assets/EncryptedData.asset"
                        };
                        //and build the bundle
                        BuildPipeline.BuildAssetBundles(encryptedOutputPath, encryptedBuildMap, BuildAssetBundleOptions.None, EditorUserBuildSettings.activeBuildTarget);
                    }
                    //save a json index in there too
                    var thisIndexJsonEncPath = Path.Combine(encryptedOutputPath, Utility.GetPlatformName().ToLower()) + "indexencrypted.json";
                    File.WriteAllText(thisIndexJsonEncPath, thisIndexJson);
                    AssetDatabase.DeleteAsset(thisEncryptionAssetPath);
                }
                //Now we can remove the temp Index item from the assetDatabase
                AssetDatabase.DeleteAsset(thisIndexAssetPath);
                //And remove its assetBundle name from the assetBundleNames
                AssetDatabase.RemoveAssetBundleName(Utility.GetPlatformName().ToLower() + "index", false);
                Debug.Log("Asset Bundles built successfully for platform " + Utility.GetPlatformName() + (UMAABMSettings.GetEncryptionEnabled() ? " (Encrypted)!" : "!"));
            }
            catch (System.Exception e)
            {
                if (thisIndexAssetPath != "")
                {
                    AssetDatabase.DeleteAsset(thisIndexAssetPath);
                }
                if (thisEncryptionAssetPath != "")
                {
                    AssetDatabase.DeleteAsset(thisEncryptionAssetPath);
                }
                //And remove its assetBundle name from the assetBundleNames
                AssetDatabase.RemoveAssetBundleName(Utility.GetPlatformName().ToLower() + "index", false);
                Debug.LogError("Your AssetBundles did not build properly. Error Message: " + e.Message + " Error Exception: " + e.InnerException + " Error StackTrace: " + e.StackTrace);
            }
        }
        void OnGUI()
        {
            scrollPos = EditorGUILayout.BeginScrollView(scrollPos, false, true);
            EditorGUILayout.BeginVertical(GUILayout.Width(EditorGUIUtility.currentViewWidth - 20f));
            EditorGUILayout.Space();
            GUILayout.Label("UMA AssetBundle Manager", EditorStyles.boldLabel);

            BeginVerticalPadded(5, new Color(0.75f, 0.875f, 1f));

            GUILayout.Label("AssetBundle Options", EditorStyles.boldLabel);
            //AssetBundle Build versioning
            EditorGUI.BeginChangeCheck();
            _enableBundleIndexVersioning = EditorGUILayout.ToggleLeft("Enable AssetBundle Index Versioning", _enableBundleIndexVersioning);
            if (EditorGUI.EndChangeCheck())
            {
                UMAABMSettings.EnableBundleIndexVersioning = _enableBundleIndexVersioning;
            }
            if (_enableBundleIndexVersioning)
            {
                BeginVerticalIndented(10, new Color(0.75f, 0.875f, 1f));
                EditorGUILayout.HelpBox("Sets the 'bundlesPlayerVersion' value of the AssetBundleIndex when you build your bundles. You can use this to determine if your app needs to force the user to go online to update their bundles and/or application (its up to you how you do that though!). ", MessageType.Info);
                EditorGUI.BeginChangeCheck();
                _bundleIndexVersioningMethod = (UMAABMSettingsStore.BundleIndexVersioningOpts)EditorGUILayout.EnumPopup("Bundles Versioning Method", _bundleIndexVersioningMethod);
                if (EditorGUI.EndChangeCheck())
                {
                    UMAABMSettings.BundleIndexVersioningMethod = _bundleIndexVersioningMethod;
                }
                if (_bundleIndexVersioningMethod == UMAABMSettingsStore.BundleIndexVersioningOpts.Custom)
                {
                    EditorGUI.BeginChangeCheck();
                    _bundleIndexCustomValue = EditorGUILayout.TextField("Bundles Index Player Version", _bundleIndexCustomValue);
                    if (EditorGUI.EndChangeCheck())
                    {
                        UMAABMSettings.BundleIndexCustomValue = _bundleIndexCustomValue;
                    }
                }
                else
                {
                    var currentBuildVersion = Application.version;
                    if (string.IsNullOrEmpty(currentBuildVersion))
                    {
                        EditorGUILayout.HelpBox("Please be sure to set a 'Version' number (eg 1.0, 2.1.0) in Edit->ProjectSettings->Player->Version", MessageType.Warning);
                    }
                    else
                    {
                        EditorGUILayout.HelpBox("Current 'Version' number (" + currentBuildVersion + ") will be used. You can change this in Edit->ProjectSettings->Player->Version.", MessageType.Info);
                    }
                }
                EditorGUILayout.Space();
                EndVerticalIndented();
            }
            //Asset Bundle Encryption
            //defined here so we can modify the message if encryption settings change
            string      buildBundlesMsg     = "";
            MessageType buildBundlesMsgType = MessageType.Info;

            EditorGUI.BeginChangeCheck();
            encryptionKeysEnabled = EditorGUILayout.ToggleLeft("Enable AssetBundle Encryption", encryptionKeysEnabled);
            if (EditorGUI.EndChangeCheck())
            {
                //If encryption was turned ON generate the encryption password if necessary
                if (encryptionKeysEnabled)
                {
                    if (currentEncryptionPassword == "")
                    {
                        if (UMAABMSettings.GetEncryptionPassword() != "")
                        {
                            currentEncryptionPassword = UMAABMSettings.GetEncryptionPassword();
                        }
                        else
                        {
                            currentEncryptionPassword = EncryptionUtil.GenerateRandomPW();
                        }
                    }
                    UMAABMSettings.SetEncryptionPassword(currentEncryptionPassword);
                    buildBundlesMsg     = "You have turned on encryption and need to Rebuild your bundles to encrypt them.";
                    buildBundlesMsgType = MessageType.Warning;
                }
                else
                {
                    UMAABMSettings.DisableEncryption();
                    currentEncryptionPassword = "";
                }
            }
            if (encryptionKeysEnabled)
            {
                BeginVerticalIndented(10, new Color(0.75f, 0.875f, 1f));
                //tip
                EditorGUILayout.HelpBox("Make sure you turn on 'Use Encrypted Bundles' in the 'DynamicAssetLoader' components in your scenes.", MessageType.Info);
                //Encryption key
                //If we can work out a way for people to download a key we can use this tip and the 'EncryptionKeyURL' field
                //string encryptionKeyToolTip = "This key is used to generate the required encryption keys used when encrypting your bundles. If you change this key you will need to rebuild your bundles otherwise they wont decrypt. If you use the 'Encryption Key URL' field below you MUST ensure this field is set to the same key the url will return.";
                string encryptionKeyToolTip = "This key is used to generate the required encryption keys used when encrypting your bundles. If you change this key you will need to rebuild your bundles otherwise they wont decrypt.";
                EditorGUILayout.LabelField(new GUIContent("Bundle Encryption Password", encryptionKeyToolTip));
                EditorGUILayout.BeginHorizontal();
                if (!manualEditEncryptionKey)
                {
                    if (GUILayout.Button(new GUIContent("Edit", encryptionKeyToolTip)))
                    {
                        manualEditEncryptionKey = true;
                    }
                    EditorGUI.BeginDisabledGroup(!manualEditEncryptionKey);
                    EditorGUILayout.TextField("", UMAABMSettings.GetEncryptionPassword());                    //THis bloody field WILL NOT update when you click edit, then canel, the value stays
                    EditorGUI.EndDisabledGroup();
                }
                else
                {
                    EditorGUI.BeginChangeCheck();
                    newEncryptionPassword = EditorGUILayout.TextArea(newEncryptionPassword);
                    if (EditorGUI.EndChangeCheck())
                    {
                        encryptionSaveButEnabled = EncryptionUtil.PasswordValid(newEncryptionPassword);
                    }
                    if (encryptionSaveButEnabled)
                    {
                        if (GUILayout.Button(new GUIContent("Save"), GUILayout.MaxWidth(60)))
                        {
                            currentEncryptionPassword = newEncryptionPassword;
                            UMAABMSettings.SetEncryptionPassword(newEncryptionPassword);
                            EditorGUIUtility.keyboardControl = 0;
                            manualEditEncryptionKey          = false;
                        }
                    }
                    else
                    {
                        GUI.enabled = false;
                        if (GUILayout.Button(new GUIContent("Save", "Your Encryptiom Password should be at least 16 characters long"), GUILayout.MaxWidth(60)))
                        {
                            //Do nothing
                        }
                        GUI.enabled = true;
                    }
                    if (GUILayout.Button(new GUIContent("Cancel", "Reset to previous value: " + currentEncryptionPassword), GUILayout.MaxWidth(60)))
                    {
                        manualEditEncryptionKey          = false;
                        newEncryptionPassword            = currentEncryptionPassword = UMAABMSettings.GetEncryptionPassword();
                        encryptionSaveButEnabled         = false;
                        EditorGUIUtility.keyboardControl = 0;
                    }
                }
                EditorGUILayout.EndHorizontal();
                //EncryptionKey URL
                //not sure how this would work- the delivered key would itself need to be encrypted probably
                //Encrypted bundle suffix
                string encryptionSuffixToolTip = "This suffix is appled to the end of your encrypted bundle names when they are built. Must be lower case and alphaNumeric. Cannot be empty. Defaults to " + DEFAULT_ENCRYPTION_SUFFIX;
                EditorGUILayout.LabelField(new GUIContent("Encrypted Bundle Suffix", encryptionSuffixToolTip));
                EditorGUILayout.BeginHorizontal();
                if (!manualEditEncryptionSuffix)
                {
                    if (GUILayout.Button(new GUIContent("Edit", encryptionSuffixToolTip)))
                    {
                        manualEditEncryptionSuffix = true;
                    }
                    EditorGUI.BeginDisabledGroup(!manualEditEncryptionSuffix);
                    EditorGUILayout.TextField(new GUIContent("", encryptionSuffixToolTip), currentEncryptionSuffix);
                    EditorGUI.EndDisabledGroup();
                }
                else
                {
                    newEncryptionSuffix = EditorGUILayout.TextArea(newEncryptionSuffix);
                    if (GUILayout.Button(new GUIContent("Save")))
                    {
                        if (newEncryptionSuffix != "")
                        {
                            Regex rgx          = new Regex("[^a-zA-Z0-9 -]");
                            var   suffixToSend = rgx.Replace(newEncryptionSuffix, "");
                            currentEncryptionSuffix = suffixToSend;
                            UMAABMSettings.SetEncryptionSuffix(suffixToSend.ToLower());
                            EditorGUIUtility.keyboardControl = 0;
                            manualEditEncryptionSuffix       = false;
                        }
                    }
                }
                EditorGUILayout.EndHorizontal();

                //Encode Bundle Names
                string encodeBundleNamesTooltip = "If true encrypted bundle names will be base64 encoded";
                EditorGUI.BeginChangeCheck();
                newEncodeNamesSetting = EditorGUILayout.ToggleLeft(new GUIContent("Encode Bundle Names", encodeBundleNamesTooltip), currentEncodeNamesSetting);
                if (EditorGUI.EndChangeCheck())
                {
                    currentEncodeNamesSetting = newEncodeNamesSetting;
                    UMAABMSettings.SetEncodeNames(newEncodeNamesSetting);
                }

                EndVerticalIndented();
            }
#if ENABLE_IOS_APP_SLICING
            string AppSlicingTooltip = "If true will build bundles uncompressed for use with iOS Resources Catalogs";
            EditorGUI.BeginChangeCheck();
            bool newAppSlicingSetting = EditorGUILayout.ToggleLeft(new GUIContent("Build for iOS App Slicing", AppSlicingTooltip), currentAppSlicingSetting);
            if (EditorGUI.EndChangeCheck())
            {
                currentAppSlicingSetting = newAppSlicingSetting;
                UMAABMSettings.SetBuildForSlicing(newAppSlicingSetting);
            }
#endif

            //Asset Bundle Building
            EditorGUILayout.Space();
            string buttonBuildAssetBundlesText = "Build AssetBundles";
            //Now defined above the encryption
            //string buildBundlesText = "Click the button below to build your bundles if you have not done so already.";
            string fullPathToBundles         = Path.Combine(Directory.GetParent(Application.dataPath).FullName, Utility.AssetBundlesOutputPath);
            string fullPathToPlatformBundles = Path.Combine(fullPathToBundles, Utility.GetPlatformName());

            //if we have not built any asset bundles there wont be anything in the cache to clear
            bool showClearCache = false;

            if (Directory.Exists(fullPathToPlatformBundles))
            {
                buttonBuildAssetBundlesText = "Rebuild AssetBundles";
                buildBundlesMsg             = buildBundlesMsg == "" ? "Rebuild your assetBundles to reflect your latest changes" : buildBundlesMsg;
                showClearCache = true;
            }
            else
            {
                buildBundlesMsg     = "You have not built your asset bundles for " + EditorUserBuildSettings.activeBuildTarget.ToString() + " yet. Click this button to build them.";
                buildBundlesMsgType = MessageType.Warning;
                showClearCache      = false;
            }
            EditorGUILayout.HelpBox(buildBundlesMsg, buildBundlesMsgType);
            if (GUILayout.Button(buttonBuildAssetBundlesText))
            {
                BuildScript.BuildAssetBundles();

#if UNITY_2017_1_OR_NEWER
                Caching.ClearCache();
#else
                Caching.CleanCache();
#endif
                return;
            }
            EndVerticalPadded(5);
            EditorGUILayout.Space();

            //Local AssetBundleServer
            BeginVerticalPadded(5f, new Color(0.75f, 0.875f, 1f));
            GUILayout.Label("AssetBundle Testing Server", EditorStyles.boldLabel);
            EditorGUILayout.HelpBox("Once you have built your bundles this local Testing Server can be enabled and it will load those AssetBundles rather than the files inside the project.", MessageType.Info);

            if (!BuildScript.CanRunLocally(EditorUserBuildSettings.activeBuildTarget))
            {
                EditorGUILayout.HelpBox("Builds for " + EditorUserBuildSettings.activeBuildTarget.ToString() + " cannot access this local server, but you can still use it in the editor.", MessageType.Warning);
            }

            bool updateURL = false;
            EnableLocalAssetBundleServer = EditorGUILayout.Toggle("Start Server", EnableLocalAssetBundleServer);

            //If the server is off we need to show the user a message telling them that they will have to have uploaded their bundles to an external server
            //and that they need to set the address of that server in DynamicAssetLoader
            int newPort = Port;
            EditorGUI.BeginChangeCheck();
            newPort = EditorGUILayout.IntField("Port", Port);
            if (EditorGUI.EndChangeCheck())
            {
                if (newPort != Port)
                {
                    if (_activeHost != null && _activeHost != "")
                    {
                        ActiveHost = _activeHost.Replace(":" + Port.ToString(), ":" + newPort.ToString());
                    }
                    Port = newPort;
                    UpdateHosts();
                    //we need to start the server to see if it works with this port- regardless of whether it is turned on or not.
                    if (!EnableLocalAssetBundleServer)
                    {
                        UpdateServer(true);
                    }
                    else
                    {
                        UpdateServer();
                    }
                    if (serverException == false)
                    {
                        //We can use the set IP with this port so update it
                        if (EnableLocalAssetBundleServer)
                        {
                            SimpleWebServer.ServerURL = ActiveHost;
                        }
                    }
                    else
                    {
                        //We CANT use the set IP with this port so set the saved URL to "" and tell the user the Port is in use elsewhere
                        SimpleWebServer.ServerURL    = "";
                        EnableLocalAssetBundleServer = false;
                        portError = true;
                    }
                }
            }
            if (!EnableLocalAssetBundleServer)
            {
                if (portError)
                {
                    EditorGUILayout.HelpBox("There are no hosts available for that port. Its probably in use by another application. Try another.", MessageType.Warning);
                }
                else
                {
                    EditorGUILayout.HelpBox("When the local server is not running the game will play in Simulation Mode OR if you have set the 'RemoteServerURL' for each DynamicAssetLoader, bundles will be downloaded from that location.", MessageType.Warning);
                    if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.WebGL)
                    {
                        EditorGUILayout.HelpBox("WARNING: AssetBundles in WebGL builds that you run locally WILL NOT WORK unless the local server is turned on, and you build using the button below!", MessageType.Warning);
                    }
                }
            }

            EditorGUILayout.Space();

            if (_hosts != null && _hosts.Length > 0 && EnableLocalAssetBundleServer)
            {
                if (_activeHost == null || _activeHost == "")
                {
                    ActiveHost = _hosts[0];
                }
                int      activeHostInt = 0;
                string[] hostsStrings  = new string[_hosts.Length];
                for (int i = 0; i < _hosts.Length; i++)
                {
                    hostsStrings[i] = _hosts[i].Replace("http://", "").TrimEnd(new char[] { '/' });
                    if (_hosts[i] == _activeHost)
                    {
                        activeHostInt = i;
                    }
                }
                EditorGUI.BeginChangeCheck();
                int newActiveHostInt = EditorGUILayout.Popup("Host Address:  http://", activeHostInt, hostsStrings);
                if (EditorGUI.EndChangeCheck())
                {
                    if (newActiveHostInt != activeHostInt)
                    {
                        ActiveHost = _hosts[newActiveHostInt];
                        updateURL  = true;
                    }
                }
            }
            EditorGUILayout.Space();

            if (showClearCache)            //no point in showing a button for bundles that dont exist - or is there? The user might be using a remote url to download assetbundles without the localserver?
            {
                EditorGUILayout.HelpBox("You can clear the cache to force asset bundles to be redownloaded.", MessageType.Info);

                if (GUILayout.Button("Clean the Cache"))
                {
#if UNITY_2017_1_OR_NEWER
                    _statusMessage = Caching.ClearCache() ? "Cache Cleared." : "Error clearing cache.";
#else
                    _statusMessage = Caching.CleanCache() ? "Cache Cleared." : "Error clearing cache.";
#endif
                }
                EditorGUILayout.Space();
            }

            EditorGUILayout.Space();
            GUILayout.Label("Server Status");
            if (_statusMessage != null)
            {
                EditorGUILayout.HelpBox(_statusMessage, MessageType.None);
            }

            if (SimpleWebServer.Instance != null)
            {
                //GUILayout.Label("Server Request Log");
                serverRequestLogOpen = EditorGUILayout.Foldout(serverRequestLogOpen, "Server Request Log");
                if (serverRequestLogOpen)
                {
                    EditorGUILayout.HelpBox(SimpleWebServer.Instance.GetLog(), MessageType.Info);
                }
            }
            if (updateURL)
            {
                SimpleWebServer.ServerURL = ActiveHost;
            }

            EndVerticalPadded(5);
            EditorGUILayout.Space();

            //Testing Build- only show this if we can run a build for the current platform (i.e. if its not iOS or Android)
            if (BuildScript.CanRunLocally(EditorUserBuildSettings.activeBuildTarget))
            {
                BeginVerticalPadded(5, new Color(0.75f, 0.875f, 1f));
                GUILayout.Label("Local Testing Build", EditorStyles.boldLabel);
                //if the bundles are built and the server is turned on then the user can use this option otherwise there is no point
                //But we will show them that this option is available even if this is not the case
                if (!showClearCache || !EnableLocalAssetBundleServer)
                {
                    EditorGUI.BeginDisabledGroup(true);
                }
                EditorGUILayout.HelpBox("Make a testing Build that uses the Local Server using the button below.", MessageType.Info);

                developmentBuild = EditorGUILayout.Toggle("Development Build", developmentBuild);
                if (GUILayout.Button("Build and Run!"))
                {
                    BuildScript.BuildAndRunPlayer(developmentBuild);
                }
                if (!showClearCache || !EnableLocalAssetBundleServer)                  //
                {
                    EditorGUI.EndDisabledGroup();
                }
                EditorGUILayout.Space();
                EndVerticalPadded(5);

                EditorGUILayout.Space();
            }
            //END SCROLL VIEW
            //for some reason when we build or build assetbundles when this window is open we get an error
            //InvalidOperationException: Operation is not valid due to the current state of the object
            //so try catch is here as a nasty hack to get rid of it
            try
            {
                EditorGUILayout.EndVertical();
                EditorGUILayout.EndScrollView();
            }
            catch { }
        }