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)); }