public CreateWorldResult(SubscribedItem world, MyWorkshopPathInfo pathInfo, Action<bool, string> callback, bool overwrite)
 {
     Callback = callback;
     Task = Parallel.Start(() =>
     {
         Success = TryCreateWorldInstanceBlocking(world, pathInfo, out m_createdSessionPath, overwrite);
     });
 }
        /// <summary>
        /// Do NOT call this method from update thread.
        /// </summary>
        private static bool GetSubscribedItemsBlocking(List<SubscribedItem> results, string tag)
        {
            results.Clear();
            Dictionary<ulong, SubscribedItem> resultsByPublishedId = new Dictionary<ulong, SubscribedItem>();
            using (ManualResetEvent mrEvent = new ManualResetEvent(false))
            {
                if (MyPerGameSettings.WorkshopUseUGCEnumerate) //temporary because of UGC bug
                {
                    uint num = MySteam.API.UGC.GetNumSubscribedItems();
                    if (num == 0)
                        return true;
                    ulong[] ids = new ulong[num];
                    MySandboxGame.Log.WriteLine(string.Format("Asking steam for {0} subscribed items", num));
                    var ret = MySteam.API.UGC.GetSubscribedItems(ids, num);
                    MySandboxGame.Log.WriteLine(string.Format("Steam returned {0} subscribed items", ret));

                    if (ret == 0)
                        return true;

                    foreach (var id in ids)
                    {
                        resultsByPublishedId[id] = new SubscribedItem() { PublishedFileId = id };
                    }
                }
                else // UGC crashes for ME workshop probably because its private
                {
                    int processedCount = 0;
                    int totalItems = 0;
                    // Retrieve PublishedFileId for each subscription.
                    Action<bool, RemoteStorageEnumerateUserSubscribedFilesResult> onEnumerateCallResult = delegate(bool ioFailure, RemoteStorageEnumerateUserSubscribedFilesResult data)
                    {
                        if (!ioFailure && data.Result == Result.OK)
                        {
                            StringBuilder buffer = new StringBuilder("Obtained subscribed files: ");
                            processedCount += data.ResultsReturned;
                            totalItems = data.TotalResultCount;
                            for (int i = 0; i < data.ResultsReturned; ++i)
                            {
                                buffer.Append(data[i]).Append(',');
                                resultsByPublishedId[data[i]] = new SubscribedItem() { PublishedFileId = data[i] };
                            }
                            MySandboxGame.Log.WriteLine(string.Format("ResultsReturned = {0}, processedCount = {1}, totalItems = {2}", data.ResultsReturned, processedCount, totalItems));
                            MySandboxGame.Log.WriteLine(buffer.ToString());
                        }
                        else
                        {
                            totalItems = -1;
                            MySandboxGame.Log.WriteLine(string.Format("Error enumerating user subscribed files. {0}", GetErrorString(ioFailure, data.Result)));
                        }

                        mrEvent.Set();
                    };

                    do
                    {
                        if (!MySteam.IsOnline)
                            return false;

                        MySteam.API.RemoteStorage.EnumerateUserSubscribedFiles((uint)processedCount, onEnumerateCallResult);

                        MySandboxGame.Log.WriteLine(string.Format("Waiting for steam response. processedCount = {0}, totalItems = {1}", processedCount, totalItems));
                        if (!mrEvent.WaitOne(30 * 1000)) // timeout
                            return false;
                        MySandboxGame.Log.WriteLine(string.Format("Got response from steam.    processedCount = {0}, totalItems = {1}", processedCount, totalItems));
                        mrEvent.Reset();
                    }
                    while (processedCount < totalItems);

                    if (totalItems == -1)
                        return false;

                    if (totalItems == 0)
                        return true;
                }

                // Retrieve details for each subscription.
                int callResultCounter = 0;
                int expectedResults = resultsByPublishedId.Count;
                var listToRemove = new List<UInt64>(resultsByPublishedId.Count - expectedResults);
                Action<bool, RemoteStorageGetPublishedFileDetailsResult> onGetDetailsCallResult = delegate(bool ioFailure, RemoteStorageGetPublishedFileDetailsResult data)
                {
                    MySandboxGame.Log.WriteLine(string.Format("Obtained details: Id={4}; Result={0}; ugcHandle={1}; title='{2}'; tags='{3}'", data.Result, data.FileHandle, data.Title, data.Tags, data.PublishedFileId));
                    if (!ioFailure && data.Result == Result.OK && data.Tags.ToLowerInvariant().Split(',').Contains(tag.ToLowerInvariant()))
                    {
                        resultsByPublishedId[data.PublishedFileId].Title = data.Title;
                        resultsByPublishedId[data.PublishedFileId].Description = data.Description;
                        resultsByPublishedId[data.PublishedFileId].UGCHandle = data.FileHandle;
                        resultsByPublishedId[data.PublishedFileId].SteamIDOwner = data.SteamIDOwner;
                        resultsByPublishedId[data.PublishedFileId].TimeUpdated = data.TimeUpdated;
                        resultsByPublishedId[data.PublishedFileId].Tags = data.Tags.Split(',');
                        ++callResultCounter;
                    }
                    else
                    {
                        bool exists = resultsByPublishedId.ContainsKey(data.PublishedFileId);
                        if (exists)
                        {
                            listToRemove.Add(data.PublishedFileId);
                        }
                        else
                        {
                            MySandboxGame.Log.WriteLine(string.Format("Nonexistent PublishedFileId reported in error. Id={0}, Error={1}",
                                data.PublishedFileId, GetErrorString(ioFailure, data.Result)));
                        }
                        // Workaround for when steam doesn't report correct published file id and thread hangs as a result.
                        expectedResults = expectedResults - 1;
                    }

                    if (callResultCounter == expectedResults)
                    {
                        try { mrEvent.Set(); }
                        catch (System.ObjectDisposedException ex)
                        {
                            MySandboxGame.Log.WriteLine(ex);
                        }
                    }
                };

                foreach (var resultEntry in resultsByPublishedId)
                {
                    if (!MySteam.IsOnline)
                        return false;

                    MySandboxGame.Log.WriteLine(string.Format("Querying details of file " + resultEntry.Value.PublishedFileId));
                    MySteam.API.RemoteStorage.GetPublishedFileDetails(resultEntry.Value.PublishedFileId, 0, onGetDetailsCallResult);
                }

                if (!mrEvent.WaitOne(60000))
                {
                    Debug.Fail("Couldn't obtain all results before timeout.");
                }

                foreach (var item in listToRemove)
                    resultsByPublishedId.Remove(item);
                listToRemove.Clear();

                if (expectedResults != resultsByPublishedId.Count)
                {// Steam reported some wrong IDs so I have to search for those missing title (and other data) and remove them here
                    int newCapacity = resultsByPublishedId.Count - expectedResults;
                    if (listToRemove.Capacity < newCapacity)
                        listToRemove.Capacity = newCapacity;
                    foreach (var entry in resultsByPublishedId)
                    {
                        if (entry.Value.Title == null)
                            listToRemove.Add(entry.Key);
                    }

                    StringBuilder sb = new StringBuilder();
                    foreach (var id in listToRemove)
                    {
                        sb.Append(id).Append(", ");
                        resultsByPublishedId.Remove(id);
                    }
                    MySandboxGame.Log.WriteLine(string.Format("Ids messed up by Steam: {0}", sb.ToString()));
                }

                results.InsertRange(0, resultsByPublishedId.Values);
            }

            return true;
        }
        /// <summary>
        /// Do NOT call this method from update thread.
        /// </summary>
        public static bool TryCreateWorldInstanceBlocking(SubscribedItem world, MyWorkshopPathInfo pathInfo, out string sessionPath, bool overwrite)
        {
            m_stop = false;
            if (!Directory.Exists(pathInfo.Path))
                Directory.CreateDirectory(pathInfo.Path);

            string safeName = MyUtils.StripInvalidChars(world.Title);
            sessionPath = null;

            var localPackedWorldFullPath = Path.Combine(pathInfo.Path, world.PublishedFileId + pathInfo.Suffix);

            if (!MySteam.IsOnline)
                return false;

            if (!IsModUpToDateBlocking(localPackedWorldFullPath, world, true))
            {
                if (!DownloadItemBlocking(localPackedWorldFullPath, world.UGCHandle))
                    return false;
            }

            // Extract packaged world.
            sessionPath = MyLocalCache.GetSessionSavesPath(safeName, false, false);

            //overwrite?
            if (overwrite && Directory.Exists(sessionPath))
                Directory.Delete(sessionPath, true);

            // Find new non existing folder. The game folder name may be different from game name, so we have to
            // make sure we don't overwrite another save
            while (Directory.Exists(sessionPath))
                sessionPath = MyLocalCache.GetSessionSavesPath(safeName + MyUtils.GetRandomInt(int.MaxValue).ToString("########"), false, false);

            MyZipArchive.ExtractToDirectory(localPackedWorldFullPath, sessionPath);

            // Update some meta-data of the new world.
            ulong checkPointSize;
            var checkpoint = MyLocalCache.LoadCheckpoint(sessionPath, out checkPointSize);
            checkpoint.SessionName = string.Format("({0}) {1}", pathInfo.NamePrefix, world.Title);
            checkpoint.LastSaveTime = DateTime.Now;
            checkpoint.WorkshopId = null;
            MyLocalCache.SaveCheckpoint(checkpoint, sessionPath);
            MyLocalCache.SaveLastLoadedTime(sessionPath, DateTime.Now);

            return true;
        }
        /// <summary>
        /// Do NOT call this method from update thread.
        /// </summary>
        public static bool TryCreateBattleWorldInstanceBlocking(SubscribedItem world, string workshopBattleWorldsPath, out string sessionPath)
        {
            if (!Directory.Exists(m_workshopWorldsPath))
                Directory.CreateDirectory(m_workshopWorldsPath);

            string safeName = MyUtils.StripInvalidChars(world.Title);
            sessionPath = null;

            var localPackedWorldFullPath = Path.Combine(m_workshopWorldsPath, world.PublishedFileId + m_workshopWorldSuffix);

            if (!MySteam.IsOnline)
                return false;

            if (!IsModUpToDateBlocking(localPackedWorldFullPath, world, true))
            {
                if (!DownloadItemBlocking(localPackedWorldFullPath, world.UGCHandle))
                    return false;
            }

            if (!Directory.Exists(workshopBattleWorldsPath))
                Directory.CreateDirectory(workshopBattleWorldsPath);

            sessionPath = Path.Combine(workshopBattleWorldsPath, safeName);

            // Find new non existing folder. The game folder name may be different from game name, so we have to
            // make sure we don't overwrite another save
            while (Directory.Exists(sessionPath))
                sessionPath = Path.Combine(workshopBattleWorldsPath, safeName + MyUtils.GetRandomInt(int.MaxValue).ToString("########"));

            MyZipArchive.ExtractToDirectory(localPackedWorldFullPath, sessionPath);

            // Update some meta-data of the new world.
            ulong checkPointSize;
            var checkpoint = MyLocalCache.LoadCheckpoint(sessionPath, out checkPointSize);
            checkpoint.SessionName = world.Title;
            checkpoint.LastSaveTime = DateTime.Now;
            checkpoint.WorkshopId = world.PublishedFileId;
            MyLocalCache.SaveCheckpoint(checkpoint, sessionPath);
            MyLocalCache.SaveLastLoadedTime(sessionPath, DateTime.Now);

            return true;
        }
 public static bool GenerateModInfo(string modPath, SubscribedItem mod)
 {
     return GenerateModInfo(modPath, mod.PublishedFileId, mod.SteamIDOwner);
 }
 public static void CreateWorldInstanceAsync(SubscribedItem world, MyWorkshopPathInfo pathInfo, bool overwrite, Action<bool, string> callbackOnFinished = null)
 {
     MyGuiSandbox.AddScreen(new MyGuiScreenProgressAsync(MyCommonTexts.ProgressTextCreatingWorld,
         null,
         () => new CreateWorldResult(world, pathInfo, callbackOnFinished, overwrite),
         endActionCreateWorldInstance));
 }
        //dont even try to understand the following function

        /// <summary>
        /// Do NOT call this method from update thread.
        /// </summary>
        public static bool DownloadWorldModsBlocking(List<MyObjectBuilder_Checkpoint.ModItem> mods)
        {
            if (!MyFakes.ENABLE_WORKSHOP_MODS)
                return true;

            MySandboxGame.Log.WriteLine("Downloading world mods - START");
            MySandboxGame.Log.IncreaseIndent();

            m_stop = false;

            var result = true;
            if (mods != null && mods.Count > 0)
            {
                var publishedFileIds = new List<ulong>();
                foreach (var mod in mods)
                {
                    if (mod.PublishedFileId != 0)
                    {
                        publishedFileIds.Add(mod.PublishedFileId);
                    }
                    else if (MySandboxGame.IsDedicated)
                    {
                        MySandboxGame.Log.WriteLineAndConsole("Local mods are not allowed in multiplayer.");
                        MySandboxGame.Log.DecreaseIndent();
                        return false;
                    }
                }

                if (MySandboxGame.IsDedicated)
                {
                    using (ManualResetEvent mrEvent = new ManualResetEvent(false))
                    {
                        string xml = "";

                        MySteamWebAPI.GetPublishedFileDetails(publishedFileIds, delegate(bool success, string data)
                        {
                            if (!success)
                            {
                                MySandboxGame.Log.WriteLine("Could not retrieve mods details.");
                            }
                            else
                            {
                                xml = data;
                            }
                            result = success;
                            mrEvent.Set();
                        });

                        while (!mrEvent.WaitOne(17))
                        {
                            mrEvent.Reset();
                            if (MySteam.Server != null)
                                MySteam.Server.RunCallbacks();
                            else
                            {
                                MySandboxGame.Log.WriteLine("Steam server API unavailable");
                                result = false;
                                break;
                            }
                        }
                        if (result == true)
                        {
                            try
                            {
                                System.Xml.XmlReaderSettings settings = new System.Xml.XmlReaderSettings()
                                {
                                    DtdProcessing = System.Xml.DtdProcessing.Ignore,
                                };
                                using (System.Xml.XmlReader reader = System.Xml.XmlReader.Create(new StringReader(xml), settings))
                                {
                                    reader.ReadToFollowing("result");

                                    Result xmlResult = (Result)reader.ReadElementContentAsInt();

                                    if (xmlResult != Result.OK)
                                    {
                                        MySandboxGame.Log.WriteLine(string.Format("Failed to download mods: result = {0}", xmlResult));
                                        result = false;
                                    }

                                    reader.ReadToFollowing("resultcount");
                                    int count = reader.ReadElementContentAsInt();

                                    if (count != publishedFileIds.Count)
                                    {
                                        MySandboxGame.Log.WriteLine(string.Format("Failed to download mods details: Expected {0} results, got {1}", publishedFileIds.Count, count));
                                    }

                                    var array = mods.ToArray();

                                    for (int i = 0; i < array.Length; ++i)
                                    {
                                        array[i].FriendlyName = array[i].Name;
                                    }

                                    var processed = new List<ulong>(publishedFileIds.Count);

                                    for (int i = 0; i < publishedFileIds.Count; ++i)
                                    {
                                        mrEvent.Reset();

                                        reader.ReadToFollowing("publishedfileid");
                                        ulong publishedFileId = Convert.ToUInt64(reader.ReadElementContentAsString());

                                        if (processed.Contains(publishedFileId))
                                        {
                                            MySandboxGame.Log.WriteLineAndConsole(string.Format("Duplicate mod: id = {0}", publishedFileId));
                                            continue;
                                        }
                                        processed.Add(publishedFileId);

                                        reader.ReadToFollowing("result");
                                        Result itemResult = (Result)reader.ReadElementContentAsInt();

                                        if (itemResult != Result.OK)
                                        {
                                            MySandboxGame.Log.WriteLineAndConsole(string.Format("Failed to download mod: id = {0}, result = {1}", publishedFileId, itemResult));
                                            result = false;
                                            continue;
                                        }

                                        reader.ReadToFollowing("consumer_app_id");
                                        int appid = reader.ReadElementContentAsInt();
                                        if (appid != MySteam.AppId)
                                        {
                                            MySandboxGame.Log.WriteLineAndConsole(string.Format("Failed to download mod: id = {0}, wrong appid, got {1}, expected {2}", publishedFileId, appid, MySteam.AppId));
                                            result = false;
                                            continue;
                                        }

                                        reader.ReadToFollowing("file_size");
                                        long fileSize = reader.ReadElementContentAsLong();

                                        reader.ReadToFollowing("file_url");
                                        string url = reader.ReadElementContentAsString();

                                        reader.ReadToFollowing("title");
                                        string title = reader.ReadElementContentAsString();

                                        for (int j = 0; j < array.Length; ++j)
                                        {
                                            if (array[j].PublishedFileId == publishedFileId)
                                            {
                                                array[j].FriendlyName = title;
                                                break;
                                            }
                                        }

                                        reader.ReadToFollowing("time_updated");
                                        uint timeUpdated = (uint)reader.ReadElementContentAsLong();

                                        var mod = new SubscribedItem() { Title = title, PublishedFileId = publishedFileId, TimeUpdated = timeUpdated };

                                        if (IsModUpToDateBlocking(Path.Combine(MyFileSystem.ModsPath, publishedFileId.ToString() + ".sbm"), mod, false, fileSize))
                                        {
                                            MySandboxGame.Log.WriteLineAndConsole(string.Format("Up to date mod:  id = {0}", publishedFileId));
                                            continue;
                                        }

                                        MySandboxGame.Log.WriteLineAndConsole(string.Format("Downloading mod: id = {0}, size = {1,8:0.000} MiB", publishedFileId, (double)fileSize / 1024f / 1024f));

                                        if (fileSize > 10 * 1024 * 1024) // WTF Steam
                                        {
                                            if (!DownloadModFromURLStream(url, publishedFileId, delegate(bool success)
                                            {
                                                if (!success)
                                                {
                                                    MySandboxGame.Log.WriteLineAndConsole(string.Format("Could not download mod: id = {0}, url = {1}", publishedFileId, url));
                                                }
                                                mrEvent.Set();
                                            }))
                                            {
                                                result = false;
                                                break;
                                            }
                                        }
                                        else
                                        {
                                            if (!DownloadModFromURL(url, publishedFileId, delegate(bool success)
                                            {
                                                if (!success)
                                                {
                                                    MySandboxGame.Log.WriteLineAndConsole(string.Format("Could not download mod: id = {0}, url = {1}", publishedFileId, url));
                                                }
                                                mrEvent.Set();
                                            }))
                                            {
                                                result = false;
                                                break;
                                            }
                                        }

                                        while (!mrEvent.WaitOne(17))
                                        {
                                            mrEvent.Reset();
                                            if (MySteam.Server != null)
                                                MySteam.Server.RunCallbacks();
                                            else
                                            {
                                                MySandboxGame.Log.WriteLine("Steam server API unavailable");
                                                result = false;
                                                break;
                                            }
                                        }
                                    }
                                    mods.Clear();
                                    mods.AddArray(array);
                                }
                            }
                            catch (Exception e)
                            {
                                MySandboxGame.Log.WriteLine(string.Format("Failed to download mods: {0}", e));
                                result = false;
                            }
                        }
                    }
                }
                else // client
                {
                    var toGet = new List<SubscribedItem>(publishedFileIds.Count);

                    if (!GetItemsBlocking(toGet, publishedFileIds))
                    {
                        MySandboxGame.Log.WriteLine("Could not obtain workshop item details");
                        result = false;
                    }
                    else if (publishedFileIds.Count != toGet.Count)
                    {
                        MySandboxGame.Log.WriteLine(string.Format("Could not obtain all workshop item details, expected {0}, got {1}", publishedFileIds.Count, toGet.Count));
                        result = false;
                    }
                    else
                    {
                        m_asyncDownloadScreen.ProgressText = MyCommonTexts.ProgressTextDownloadingMods;

                        if (!DownloadModsBlocking(toGet))
                        {
                            MySandboxGame.Log.WriteLine("Downloading mods failed");
                            result = false;
                        }
                        else
                        {
                            var array = mods.ToArray();

                            for (int i = 0; i < array.Length; ++i)
                            {
                                var mod = toGet.Find(x => x.PublishedFileId == array[i].PublishedFileId);
                                if (mod != null)
                                {
                                    array[i].FriendlyName = mod.Title;
                                }
                                else
                                {
                                    array[i].FriendlyName = array[i].Name;
                                }
                            }
                            mods.Clear();
                            mods.AddArray(array);
                        }
                    }
                }
            }
            MySandboxGame.Log.DecreaseIndent();
            MySandboxGame.Log.WriteLine("Downloading world mods - END");

            return result;
        }
        /// <summary>
        /// Do NOT call this method from update thread.
        /// </summary>
        private static bool IsModUpToDateBlocking(string fullPath, SubscribedItem mod, bool checkWithWorkshop, long expectedFilesize = -1)
        {
            if (m_stop)
                return false;

            if (!File.Exists(fullPath))
                return false;

            if (checkWithWorkshop)
            {
                var success = false;

                using (ManualResetEvent mrEvent = new ManualResetEvent(false))
                {
                    MySteam.API.RemoteStorage.GetPublishedFileDetails(mod.PublishedFileId, 0, delegate(bool ioFailure, RemoteStorageGetPublishedFileDetailsResult data)
                    {
                        success = !ioFailure && data.Result == Result.OK;
                        if (success)
                        {
                            mod.TimeUpdated = data.TimeUpdated;
                            expectedFilesize = data.FileSize;
                        }
                        else
                        {
                            MySandboxGame.Log.WriteLine(string.Format("Error downloading file details: Id={0}, {1}", mod.PublishedFileId, GetErrorString(ioFailure, data.Result)));
                        }
                        mrEvent.Set();
                    });

                    success = mrEvent.WaitOne(60000);
                }

                if (!success)
                    return false;
            }

            if (expectedFilesize != -1)
            {
                using (var file = File.OpenRead(fullPath))
                {
                    if (file.Length != expectedFilesize)
                    {
                        return false;
                    }
                }
            }

            var localTimeUpdated = File.GetLastWriteTimeUtc(fullPath);
            var remoteTimeUpdated = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(mod.TimeUpdated);

            return localTimeUpdated >= remoteTimeUpdated;
        }
        public static bool IsBlueprintUpToDate(SubscribedItem item)
        {  
            if (!MySteam.IsOnline)
                return false;

            if (m_stop)
                return false;

            var localPackedModFullPath = Path.Combine(m_workshopBlueprintsPath, item.PublishedFileId + m_workshopBlueprintSuffix);

            return IsModUpToDateBlocking(localPackedModFullPath, item, true);
        }
        public static bool DownloadBlueprintBlocking(SubscribedItem item,bool check =true)
        {
            if (m_stop)
                return false;

            var localPackedModFullPath = Path.Combine(m_workshopBlueprintsPath, item.PublishedFileId + m_workshopBlueprintSuffix);

            if (check == false || !IsModUpToDateBlocking(localPackedModFullPath, item, true))
            {
                if (!DownloadItemBlocking(localPackedModFullPath, item.UGCHandle))
                {
                    return false;
                }
            }
            else
            {
                MySandboxGame.Log.WriteLineAndConsole(string.Format("Up to date mod: Id = {0}, title = '{1}'", item.PublishedFileId, item.Title));
            }
            return true;
        }
        public static bool DownloadScriptBlocking(SubscribedItem item)
        {
            if (!MySteam.IsOnline)
                return false;

            if (m_stop)
                return false;

            var localPackedModFullPath = Path.Combine(m_workshopScriptPath, item.PublishedFileId + MyGuiIngameScriptsPage.WORKSHOP_SCRIPT_EXTENSION);

            if (!IsModUpToDateBlocking(localPackedModFullPath, item, true))
            {
                if (!DownloadItemBlocking(localPackedModFullPath, item.UGCHandle))
                {
                    return false;
                }
            }
            else
            {
                MySandboxGame.Log.WriteLineAndConsole(string.Format("Up to date mod: Id = {0}, title = '{1}'", item.PublishedFileId, item.Title));
            }
            return true;
        }
 public CreateWorldResult(SubscribedItem world, Action<bool, string> callback)
 {
     Callback = callback;
     Task = Parallel.Start(() =>
     {
         Success = TryCreateWorldInstanceBlocking(world, out m_createdSessionPath);
     });
 }
 public static void CreateWorldInstanceAsync(SubscribedItem world, Action<bool, string> callbackOnFinished = null)
 {
     MyGuiSandbox.AddScreen(new MyGuiScreenProgressAsync(MySpaceTexts.ProgressTextCreatingWorld,
         null,
         () => new CreateWorldResult(world, callbackOnFinished),
         endActionCreateWorldInstance));
 }