public void StartDownloadThread(Bundle bundleInfo)
        {
            if (m_isApplicationQuitting)
            {
                return;
            }
            if (!ShouldDownloadWithCurrentConnection(bundleInfo))
            {
                return;
            }

            if (m_downloadThread != null && m_downloadThread.IsAlive)
            {
                return;
            }

            if (bundleInfo != null)
            {
                Debug.Log("AssetBundleDownloadManager. Starting Download: " + bundleInfo.assetBundleName);
                m_downloadThread = new Thread(() => DoDownload(bundleInfo, AssetBundleManager.Instance.CurrentReachability));
                m_downloadThread.Start();
                AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(bundleInfo.assetBundleName, AssetBundleDownloadState.Downloading));
            }
        }
        public void GetBundleCRCInformationFromServer()
        {
            Debug.Log("AssetBundleInformationManager: GetBundleCRCInformation");

            try
            {
                var request = (HttpWebRequest)HttpWebRequest.Create(AssetBundleManager.Instance.BundleServerURL + "assetbundles.crc");
                request.Timeout = 5000; //5 secs timeout

                Debug.Log("Request URI (CRC): " + request.RequestUri.AbsoluteUri);


                using (var response = (HttpWebResponse)request.GetResponse())
                {
                    m_realtimeToPollAgain += m_realtimeToPollAgain; // Take longer each time we call to prevent over polling.

                    if ((int)response.StatusCode == 200)
                    {
                        Debug.Log("AssetBundleInformationManager: LoadCRCInformation Server");

                        Stream       dataStream  = response.GetResponseStream();
                        StreamReader reader      = new StreamReader(dataStream);
                        string       strResponse = reader.ReadToEnd();
                        ParseCRCInformation(strResponse, m_serverAssetBundleCRCInformation);


                        //SERVER CRC INFO:

                        // Compare server CRC's with local CRC's for changes.
                        foreach (var newServerCrc in m_serverAssetBundleCRCInformation)
                        {
                            foreach (AssetBundleCRCInfo localCrc in m_localAssetBundleCRCInformation)
                            {
                                if (localCrc.m_name == newServerCrc.m_name)
                                {
                                    //this is an asset bundle we already know about from local crc info

                                    if (localCrc.m_fileSizeBytes != newServerCrc.m_fileSizeBytes || localCrc.m_CRC != newServerCrc.m_CRC)
                                    {
                                        Debug.Log("AssetBundleInformationManager::GetBundleCRCInformationFromServer - Detected a new version of the bundle from the server. Wiping the current file and downloading the new one.");
                                        //the server has a file that's different from the one we have here. Wipe any copy of this file, but don't count it as a failure to load
                                        AssetBundleLocalFileChecked.Invoke(this, new AssetBundleLocalFileCheckedEventArgs(localCrc.m_name, false, 0, newServerCrc.m_fileSizeBytes));
                                        AssetBundleDownloadCorruptedOrBroken.Invoke(this, new AssetBundleDeleteAssetBundleFileEventArgs(localCrc.m_name, null));
                                        AssetBundleRevokePermision.Invoke(this, new AssetBundleEventArgs(localCrc.m_name));

                                        //no point keeping old crc info for a file we never had/couldn't load. Simply replace the crc info
                                        int index = m_localAssetBundleCRCInformation.IndexOf(localCrc);
                                        m_localAssetBundleCRCInformation[index] = newServerCrc;
                                    }

                                    //otherwise it didn't change, and we can simply continue
                                    goto localCrcFound;
                                }
                            }

                            //this code is skipped if the file was found in the known assetbundle list
                            //unknown asset bundle, add it to the list
                            m_localAssetBundleCRCInformation.Add(newServerCrc);
                            AssetBundleLocalFileChecked.Invoke(this, new AssetBundleLocalFileCheckedEventArgs(newServerCrc.m_name, false, 0, newServerCrc.m_fileSizeBytes));
                            AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(newServerCrc.m_name, AssetBundleDownloadState.WaitingManualPermission, null));


                            localCrcFound :;
                        }

                        WriteCRCInfoToDisk();

                        reader.Close();
                        HasLoadedServerCRCInfo            = true;
                        m_AssetBundleCRCInformationThread = null;
                    }
                    else
                    {
                        Debug.LogError("AssetBundleInformationManager: GetBundleCRCInformation Failed With Response: " + response.StatusCode);
                        m_AssetBundleCRCInformationThread = null;
                    }
                    response.Close();
                }
            }
            catch (WebException e)
            {
                if (AssetBundleManager.Instance.CurrentReachability == NetworkReachability.NotReachable)
                {
                    Debug.LogWarning("No internet access, cannot get asset bundle data");
                    return;
                }
                m_realtimeToPollAgain += m_realtimeToPollAgain;
                Debug.LogException(e);
                Debug.LogError("AssetBundleInformationManager: GetBundleCRCInformationFromServer WebException " + e.ToString());

                m_AssetBundleCRCInformationThread = null;
            }
            catch (ThreadAbortException)
            {
                m_realtimeToPollAgain += m_realtimeToPollAgain;
                Debug.LogWarning("AssetBundleInformationManager: GetBundleCRCInformationFromServer Thread aborted. Assuming intentionally.");
            }
            catch (Exception e)
            {
                m_realtimeToPollAgain += m_realtimeToPollAgain;
                Debug.LogException(e);
                Debug.LogError("AssetBundleInformationManager: GetBundleCRCInformationFromServer Exception " + e.ToString());
            }
        }
        public void CheckLocalAssetBundle(AssetBundleCRCInfo localCrc, int localCrcIndex, AssetBundleCRCInfo?serverCrc)
        {
            bool     isInGame          = AssetBundleManager.Instance.IsInGame;
            string   fullFileName      = AssetBundleManager.Instance.CurrentVersionDownloadLocation + "/" + localCrc.m_name;
            string   tempFileName      = fullFileName + ".incomplete";
            FileInfo fullAssetFileInfo = new FileInfo(fullFileName);

            if (fullAssetFileInfo.Exists)
            {
                //check filesize first
                if (fullAssetFileInfo.Length == localCrc.m_fileSizeBytes)
                {
                    if (isInGame)
                    {
                        //the file is the correct size, but CRC is slow, so we'll delay the CRC
                        //don't do this now!
                        return;
                    }

                    Debug.Log("AssetBundleInformationManager::CheckLocalAssetBundle : Completed file: " + localCrc.m_name);
                    //sweet, same length, it's complete, now to check CRC
                    uint crc = AssetBundleUtils.GenerateCRC32FromFile(fullFileName);
                    if (crc == localCrc.m_CRC)
                    {
                        //it's the right file, untampered.
                        //it's loadable.
                        Debug.Log("AssetBundleInformationManager::CheckLocalAssetBundle : Local Asset Bundle " + localCrc.m_name + " is Loadable. SUCCESS");
                        AssetBundleLocalFileChecked.Invoke(this, new AssetBundleLocalFileCheckedEventArgs(localCrc.m_name, true, localCrc.m_fileSizeBytes, localCrc.m_fileSizeBytes));
                        AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(localCrc.m_name, AssetBundleDownloadState.Loadable, null));
                        AssetBundleRevokePermision.Invoke(this, new AssetBundleEventArgs(localCrc.m_name));
                    }
                    else
                    {
                        //tampered? or damaged? Delete it.
                        Debug.LogError("AssetBundleInformationManager::CheckLocalAssetBundle : Local Asset Bundle " + localCrc.m_name + " failed the CRC check. Deleting.");
                        AssetBundleDownloadCorruptedOrBroken.Invoke(this, new AssetBundleDeleteAssetBundleFileEventArgs(localCrc.m_name, "FAILED_LOCAL_CRC_CHECK"));
                    }
                }
                else
                {
                    //it's not an incomplete dl, and it's either too big or too small, not sure how that could happen, wipe it and force a redownload
                    //no point crcing as the bytes are definitely different.
                    Debug.LogError("AssetBundleInformationManager: Local Asset Bundle " + localCrc.m_name + " failed the LENGTH check. How!? Deleting.");
                    AssetBundleDownloadCorruptedOrBroken.Invoke(this, new AssetBundleDeleteAssetBundleFileEventArgs(localCrc.m_name, "FAILED_LOCAL_LENGTH_CHECK"));
                }
            }

            FileInfo tempAssetFileInfo = new FileInfo(tempFileName);

            if (tempAssetFileInfo.Exists)
            {
                Debug.Log("AssetBundleInformationManager::CheckLocalAssetBundle : Temporary File: " + localCrc.m_name);
                if (isInGame)
                {
                    if (serverCrc.HasValue && tempAssetFileInfo.Length < serverCrc.Value.m_fileSizeBytes)
                    {
                        // We don't know what this file is, but we'll assume it's a partially downloaded NEW asset bundle and put it back in the queue
                        Debug.Log("AssetBundleInformationManager::CheckLocalAssetBundle : Temporary Asset Bundle " + localCrc.m_name + " failed CRC. It's small enough to feasibly be a partially-downloaded new asset bundle. Download continuing.");
                        AssetBundleLocalFileChecked.Invoke(this, new AssetBundleLocalFileCheckedEventArgs(serverCrc.Value.m_name, false, tempAssetFileInfo.Length, serverCrc.Value.m_fileSizeBytes));
                        AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(serverCrc.Value.m_name, AssetBundleDownloadState.Queued, null));
                    }
                    return;
                }
                else
                {
                    uint crc = AssetBundleUtils.GenerateCRC32FromFile(tempFileName);

                    bool crcMatchesLocal  = localCrc.m_CRC == crc;
                    bool crcMatchesServer = serverCrc.HasValue && serverCrc.Value.m_CRC == crc;

                    if (crcMatchesLocal || crcMatchesServer)
                    {
                        // Crc matches either!

                        if (!crcMatchesLocal)
                        {
                            // The recently completed ".incomplete" file will now be moved to the final destination (oooh).
                            // Thus, the local CRC info needs to match the new crc.
                            m_localAssetBundleCRCInformation[localCrcIndex] = serverCrc.Value;
                        }

                        // The file is loadable, so move it to the right file name. Yay!
                        Debug.Log("AssetBundleInformationManager::CheckLocalAssetBundle : Temporary Asset Bundle " + localCrc.m_name + " matched a CRC check and is now loadable. SUCCESS." +
                                  "\nServer: " + crcMatchesServer + ", Local: " + crcMatchesLocal);

                        AssetBundleFinishedAndRequiresMoving.Invoke(this, new AssetBundleEventArgs(localCrc.m_name));
                    }
                    else if (serverCrc.HasValue && tempAssetFileInfo.Length < serverCrc.Value.m_fileSizeBytes)
                    {
                        // We don't know what this file is, but we'll assume it's a partially downloaded NEW asset bundle.
                        Debug.Log("AssetBundleInformationManager::CheckLocalAssetBundle :  Temporary Asset Bundle " + localCrc.m_name + " failed CRC. It's small enough to feasibly be a partially-downloaded new asset bundle. Download continuing.");
                        AssetBundleLocalFileChecked.Invoke(this, new AssetBundleLocalFileCheckedEventArgs(serverCrc.Value.m_name, false, tempAssetFileInfo.Length, serverCrc.Value.m_fileSizeBytes));
                        AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(serverCrc.Value.m_name, AssetBundleDownloadState.Queued, null));
                    }
                    else if (HasLoadedServerCRCInfo)
                    {
                        // It failed all CRC AND we have got all server CRC's. Thus, this file must be corrupted.
                        Debug.LogError("AssetBundleInformationManager::CheckLocalAssetBundle : Local Asset Bundle " + localCrc.m_name + " does not match any CRC, and we have all server CRC's. Assumed corrupted.");
                        AssetBundleDownloadCorruptedOrBroken.Invoke(this, new AssetBundleDeleteAssetBundleFileEventArgs(localCrc.m_name, "FAILED_SERVER_CRC_CHECK"));
                    }
                    else
                    {
                        // We don't know if this is a new version, so no checks are worth doing on this until we have server info
                        Debug.Log("AssetBundleInformationManager::CheckLocalAssetBundle : Local Asset Bundle " + localCrc.m_name + " does not match local CRC and we do not currently have the server CRC's. " +
                                  "It's probably a new bundle in a server CRC that we haven't downloaded for this play session yet. We'll queue it for download, pending the server CRC's.");
                        AssetBundleLocalFileChecked.Invoke(this, new AssetBundleLocalFileCheckedEventArgs(localCrc.m_name, false, tempAssetFileInfo.Length, localCrc.m_fileSizeBytes));
                        AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(localCrc.m_name, AssetBundleDownloadState.WaitingManualPermission, null));
                    }
                }
            }

            // CRC checking complete. Phew.

            tempAssetFileInfo.Refresh();
            fullAssetFileInfo.Refresh();
            if (!tempAssetFileInfo.Exists && !fullAssetFileInfo.Exists)
            {
                // No file left after CRC checks, so the only thing to do is begin downloading afresh.
                AssetBundleLocalFileChecked.Invoke(this, new AssetBundleLocalFileCheckedEventArgs(localCrc.m_name, false, 0, localCrc.m_fileSizeBytes));
                AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(localCrc.m_name, AssetBundleDownloadState.WaitingManualPermission, null));
            }
        }
        private void DoDownload(Bundle BundleInfo, NetworkReachability reachability)
        {
            Debug.Log("Download Thread Started: " + BundleInfo.assetBundleName);

            NetworkReachability startingReachability = reachability;
            string     downloadLocation = AssetBundleManager.Instance.CurrentVersionDownloadLocation + "/" + BundleInfo.assetBundleName;
            FileInfo   tempFileInfo     = new FileInfo(downloadLocation + ".incomplete");
            FileStream saveFileStream   = null;

            try
            {
                Debug.Log("AssetBundler DoDownload " + BundleInfo.assetBundleName + " to " + AssetBundleManager.Instance.CurrentVersionDownloadLocation);
                CreateDownloadDirectory();
                long existingLength = 0;
                if (tempFileInfo.Exists)
                {
                    existingLength = tempFileInfo.Length;
                }
                else
                {
                    //making file incomplete, to mark it for future automatic download
                    try
                    {
                        FileStream fs = tempFileInfo.Create();
                        fs.Close();
                    }
                    catch (Exception e)
                    {
                        //if we can't make a temp file, we quit. go away
                        Debug.LogException(e);
                        Debug.LogError("AssetBundler DoDownload: Critical failure in creating temp file: " + e.ToString());
                        AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(BundleInfo.assetBundleName, AssetBundleDownloadState.Blocked, "FAILED_TO_WRITE_TO_DISK"));
                        return;
                    }
                }
                Debug.Log("AssetBundler DoDownload: Resuming incomplete DL. " + existingLength + " bytes downloaded already.");

                string downloadURL = AssetBundleManager.Instance.BundleServerURL + BundleInfo.assetBundleName;
                Debug.Log("AssetBundler DoDownload: " + downloadURL);
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(downloadURL);
                request.Timeout          = 5000; //5 secs timeout
                request.ReadWriteTimeout = 5000;
                request.AddRange((int)existingLength, (int)BundleInfo.downloadSizeBytes);
                request.KeepAlive = false;

                Debug.Log("Request URI: " + request.RequestUri.AbsoluteUri);
                bool didComplete = false;
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    if (response.ContentLength == 0)
                    {
                        //no more to download from the server
                        if (existingLength >= BundleInfo.downloadSizeBytes)
                        {
                            didComplete = true;
                        }
                        else
                        {
                            throw new Exception("Error. Data on server not sufficient: " + existingLength + " bytes available, expected " + BundleInfo.downloadSizeBytes);
                        }
                    }

                    long serverFileSize = existingLength + response.ContentLength; //response.ContentLength gives me the size that is remaining to be downloaded

                    bool downloadResumable;                                        // need it for not sending any progress
                    int  responseCode = (int)response.StatusCode;
                    downloadResumable = CheckForResumeableReponseCode(BundleInfo, downloadURL, responseCode);
                    if (!downloadResumable)
                    {
                        existingLength = 0;
                    }

                    DownloadProgressChanged.Invoke(this, new DownloadProgressChangedEventArgs(BundleInfo.assetBundleName, existingLength, 0));

                    using (saveFileStream = tempFileInfo.Open(downloadResumable ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
                    {
                        using (Stream stream = response.GetResponseStream())
                        {
                            stream.ReadTimeout = 5000;

                            byte[]    downBuffer      = new byte[4096];
                            long      totalReceived   = existingLength;
                            long      sessionReceived = 0;
                            Stopwatch stopwatch       = Stopwatch.StartNew();
                            while (totalReceived < serverFileSize)
                            {
#if !PRODUCTION && !PREPRODUCTION
                                bool paused = false;// Assets.Code.Common.DebugOptions.GetToggleValue(Assets.Code.Common.DebugToggle.downloadsPaused);
                                if (paused)
                                {
                                    Thread.Sleep(100);
                                    continue;
                                }
                                DebugNoInterentConnection();

                                DebugThrottleConnectionSpeed();
#endif

                                //if connection has downgraded to a non-allowed network,
                                //or if user has changed the current prioritised download, this pauses the download
                                if (startingReachability != AssetBundleManager.Instance.CurrentReachability ||
                                    AssetBundleManager.Instance.CheckAndResetCurrentPriorityChanged() ||
                                    AssetBundleManager.Instance.CheckAndResetDownloadTimeout())
                                {
                                    AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(BundleInfo.assetBundleName, AssetBundleDownloadState.Queued));
                                    AssetBundleManager.Instance.ResetPollingTime();  //we reset the poll to start the next download immediately
                                    stopwatch.Stop();
                                    stream.Close();
                                    response.Close();
                                    saveFileStream.Close();
                                    saveFileStream = null;
                                    return;
                                }

                                int byteSize = stream.Read(downBuffer, 0, downBuffer.Length);
                                saveFileStream.Write(downBuffer, 0, byteSize);
                                totalReceived   += byteSize;
                                sessionReceived += byteSize;



                                float currentSpeed = sessionReceived / (float)stopwatch.Elapsed.TotalSeconds;
                                DownloadProgressChanged.Invoke(this, new DownloadProgressChangedEventArgs(BundleInfo.assetBundleName, totalReceived, (long)currentSpeed));
                            }
                            didComplete = true;
                            stopwatch.Stop();
                            stream.Close();
                        }
                    }
                    response.Close();
                    saveFileStream.Close();
                    saveFileStream = null;
                }
                if (didComplete)
                {
                    Debug.Log("AssetBundler DoDownload. Completed. Throwing to CRC Check.");
                    //download completed
                    AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(BundleInfo.assetBundleName, AssetBundleDownloadState.CRCCheck));
                    AssetBundleManager.Instance.ResetPollingTime(); //we reset the poll to start the next download immediately
                }
            }
            catch (WebException we)
            {
                //416 error means we requested some bytes which don't exist.
                //this means the local game is expecting more data than the server has.
                //in this case, the file must be finished (no more data on the server), so we throw it to a CRC check to confirm
                if ((int)we.Status == 416)
                {
                    Debug.LogWarning("AssetBundler DoDownload: Error 416. Assuming File is completed");
                    //this is considered an error for retry purposes
                    AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(BundleInfo.assetBundleName, AssetBundleDownloadState.CRCCheck, "ERROR_416_FROM_SERVER"));
                }
                if (we.Status == WebExceptionStatus.Timeout)
                {
                    Debug.LogException(we);
                    Debug.LogWarning("Connection Timed Out, likly due to poor or loss of connection, restarting bundle download");
                    AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(BundleInfo.assetBundleName, AssetBundleDownloadState.CRCCheck));
                }
                else
                {
                    Debug.LogException(we);
                    Debug.LogError("AssetBundler DoDownload: Exception caused assetbundle download failure " + BundleInfo.assetBundleName + " . Performing full file/CRC to work out file status: " + we.ToString());
                    AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(BundleInfo.assetBundleName, AssetBundleDownloadState.CRCCheck)); //no error because timeouts aren't a problem with the bundle
                }
            }
            catch (IOException ioe)
            {
                Debug.LogException(ioe);
                Debug.LogError("AssetBundler DoDownload: IO/Write Exception. Deleting offending file: " + tempFileInfo.Name + ": " + ioe.ToString());
                AssetBundleDownloadCorruptedOrBroken.Invoke(this, new AssetBundleDeleteAssetBundleFileEventArgs(BundleInfo.assetBundleName, "ASSETBUNDLE_IO_WRITE_EXCEPTION"));
                //kick it right back off again
            }
            catch (ObjectDisposedException objectDisposedException)
            {
                Debug.LogWarning(objectDisposedException.Message);
                Debug.LogWarning("Aborting download " + BundleInfo.assetBundleName + ", likly due to loss of connection, recheck bundle");
                AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(BundleInfo.assetBundleName, AssetBundleDownloadState.CRCCheck));
            }
            catch (ThreadAbortException)
            {
                Debug.LogWarning("AssetBundler DoDownload: Thread aborted. Assuming intentionally.");
                //AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(assetBundleQueueInfo.AssetBundleName, AssetBundleDownloadState.CRCCheck, ""));
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                Debug.LogError("AssetBundler DoDownload: Exception caused assetbundle download failure. Performing full file/CRC to work out file status. Error:  " + e.ToString());
                AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(BundleInfo.assetBundleName, AssetBundleDownloadState.CRCCheck, e.ToString()));
            }
            finally
            {
                if (saveFileStream != null)
                {
                    saveFileStream.Close();
                    saveFileStream = null;
                }
            }
            Debug.Log("Download Thread Ended: " + BundleInfo.assetBundleName);
        }
 public void ForceRecheckOfBundle(Bundle assetBundleQueueInfo)
 {
     AssetBundleStateChanged.Invoke(this, new AssetBundleStateChangedEventArgs(assetBundleQueueInfo.assetBundleName, AssetBundleDownloadState.CRCCheck));
 }