// This can process passed in dependencies, either as the workshop id (ulong), or a path. // If a path is passed in, then an attempt will be made to grab the workshop id from the local mod. // Mod must be published for that to work. void ProcessDependencies(IEnumerable <string> deps, IEnumerable <string> add, IEnumerable <string> remove) { var existingDeps = m_workshopItems.GetValueOrDefault(m_modId.FirstOrDefault())?.Dependencies.ToList() ?? new List <ulong>(); // Check if the deps contains exactly one element, and that element is a 0 or "none", // if so, set the result to a list of a single ulong value of 0 // Otherwise, just fill it with the contents transformed to ids. var explicitDeps = (deps?.Count() == 1 && (deps.First() == "0" || deps.First().Equals("none", StringComparison.InvariantCultureIgnoreCase))) ? new List <ulong>() { 0 } : new List <ulong>(deps?.Select(id => ParseOrGetWorkshopID(id)) ?? new List <ulong>()); var depsToAdd = new List <ulong>(add.Select(id => ParseOrGetWorkshopID(id))); var depsToRemove = new List <ulong>(remove.Select(id => ParseOrGetWorkshopID(id))); // Steam actually requests the list of deps to add and remove explicitly, so we have to figure it out if (explicitDeps?.Count > 0) { if (explicitDeps.Count == 1 && explicitDeps[0] == 0) { // Remove ALL dependencies depsToRemove.AddRange(existingDeps); explicitDeps.Clear(); } else if (existingDeps?.Count > 0) { // Any dependencies that existed, but weren't specified, will be removed depsToRemove.AddRange(existingDeps.Except(explicitDeps)); depsToAdd.AddRange(explicitDeps.Except(existingDeps)); } } // Remove from add list any that already exist depsToAdd.RemoveAll(d => existingDeps.Contains(d)); // Remove from remove list any dependencies that don't exist, or aren't configured to set depsToRemove.RemoveAll(d => !existingDeps.Contains(d) && !(explicitDeps?.Contains(d) == true)); // Filter out items that aren't actually mods, these can crash the game if set // Don't check depsToRemove though, so users can remove invalid ones that already exist WorkshopHelper.GetItemsBlocking(explicitDeps).ForEach(i => { if (!CheckDependency(i)) { explicitDeps.Remove(i.Id); } }); WorkshopHelper.GetItemsBlocking(depsToAdd).ForEach(i => { if (!CheckDependency(i)) { depsToAdd.Remove(i.Id); } }); // Add all explicit deps to the add list that don't already exist depsToAdd.AddRange(explicitDeps.Except(existingDeps)); m_deps = existingDeps.Union(explicitDeps ?? new List <ulong>()).Union(depsToAdd).Except(depsToRemove).Where(i => i != 0).Distinct().ToArray(); m_depsToAdd = depsToAdd.Distinct().ToArray(); m_depsToRemove = depsToRemove.Distinct().ToArray(); }
static System.Threading.Tasks.Task <bool> DownloadMods(Options options) { // Get PublishItemBlocking internal method via reflection MySandboxGame.Log.WriteLineAndConsole(string.Empty); var Task = System.Threading.Tasks.Task <bool> .Factory.StartNew(() => { bool success = true; MySandboxGame.Log.WriteLineAndConsole("Beginning batch workshop download..."); MySandboxGame.Log.WriteLineAndConsole(string.Empty); if (options.Collections?.Count() > 0) { var items = new List <MyWorkshopItem>(); // get collection information options.Collections.ForEach(s => items.AddRange(WorkshopHelper.GetCollectionDetails(ulong.Parse(s)))); options.ModPaths = CombineCollectionWithList(WorkshopType.Mod, items, options.ModPaths); options.Blueprints = CombineCollectionWithList(WorkshopType.Blueprint, items, options.Blueprints); #if SE options.IngameScripts = CombineCollectionWithList(WorkshopType.IngameScript, items, options.IngameScripts); #endif options.Worlds = CombineCollectionWithList(WorkshopType.World, items, options.Worlds); options.Scenarios = CombineCollectionWithList(WorkshopType.Scenario, items, options.Scenarios); } if (!ProcessItemsDownload(WorkshopType.Mod, options.ModPaths, options)) { success = false; } if (!ProcessItemsDownload(WorkshopType.Blueprint, options.Blueprints, options)) { success = false; } #if SE if (!ProcessItemsDownload(WorkshopType.IngameScript, options.IngameScripts, options)) { success = false; } #endif if (!ProcessItemsDownload(WorkshopType.World, options.Worlds, options)) { success = false; } if (!ProcessItemsDownload(WorkshopType.Scenario, options.Scenarios, options)) { success = false; } MySandboxGame.Log.WriteLineAndConsole("Batch workshop download complete!"); return(success); }); return(Task); }
void PrintItemDetails() { const int MAX_LENGTH = 40; MySandboxGame.Log.WriteLineAndConsole(string.Format("Visibility: {0}", m_visibility)); MySandboxGame.Log.WriteLineAndConsole(string.Format("Tags: {0}", string.Join(", ", m_tags))); if (!string.IsNullOrEmpty(m_description)) { MySandboxGame.Log.WriteLineAndConsole($"Description: {m_description.Substring(0, Math.Min(m_description.Length, MAX_LENGTH))}{(m_description.Length > MAX_LENGTH ? "..." : "")}"); } if (!string.IsNullOrEmpty(m_changelog)) { MySandboxGame.Log.WriteLineAndConsole($"Changelog: {m_changelog.Substring(0, Math.Min(m_changelog.Length, MAX_LENGTH))}{(m_changelog.Length > MAX_LENGTH ? "..." : "")}"); } #if SE MySandboxGame.Log.WriteLineAndConsole(string.Format("DLC requirements: {0}", (m_dlcs?.Length > 0 ? string.Join(", ", m_dlcs.Select(i => { try { return(Sandbox.Game.MyDLCs.DLCs[i].Name); } catch { return($"Unknown({i})"); } })) : "None"))); #endif MySandboxGame.Log.WriteLineAndConsole(string.Format("Dependencies: {0}", (m_deps?.Length > 0 ? string.Empty : "None"))); if (m_deps?.Length > 0) { var width = Console.Out.IsInteractive() ? Console.WindowWidth : 256; var depIds = m_deps.ToWorkshopIds(); var depItems = WorkshopHelper.GetItemsBlocking(depIds.ToArray()); if (depItems?.Count > 0) { depItems.ForEach(i => MySandboxGame.Log.WriteLineAndConsole(string.Format("{0,15} -> {1}", i.Id, i.Title.Substring(0, Math.Min(i.Title.Length, width - 45))))); } else { MySandboxGame.Log.WriteLineAndConsole(string.Format(" {0}", string.Join(", ", m_deps))); } } MySandboxGame.Log.WriteLineAndConsole(string.Format("Thumbnail: {0}", m_previewFilename ?? "No change")); ValidateThumbnail(); }
PublishedFileVisibility GetVisibility() { var results = m_workshopItems?.Values ?? (ICollection <MyWorkshopItem>)WorkshopHelper.GetItemsBlocking(m_modId); if (results?.Count > 0) { if (results.Count > 0) { return((PublishedFileVisibility)(int)results.First().Visibility); } else { return(PublishedFileVisibility.Private); } } return(PublishedFileVisibility.Private); }
string[] GetTags() { var results = m_workshopItems?.Values ?? (ICollection <MyWorkshopItem>)WorkshopHelper.GetItemsBlocking(m_modId); if (results?.Count > 0) { if (results.Count > 0) { return(results.First().Tags.ToArray()); } else { return(null); } } return(null); }
static string[] TestPathAndMakeAbsolute(WorkshopType type, string[] paths) { for (int idx = 0; paths != null && idx < paths.Length; idx++) { // If the passed in path doesn't exist, and is relative, try to match it with the expected data directory if (!Directory.Exists(paths[idx]) && !Path.IsPathRooted(paths[idx])) { // Check if value is actually a mod id, and work remotely, if so. var newpath = Path.Combine(WorkshopHelper.GetWorkshopItemPath(type), paths[idx]); if (Directory.Exists(newpath) || !ulong.TryParse(paths[idx], out var id)) { paths[idx] = newpath; } } } return(paths); }
bool FillPropertiesFromPublished() { var results = WorkshopHelper.GetItemsBlocking(m_modId); if (results?.Count > 0) { System.Threading.Thread.Sleep(1000); // Fix for DLC not being filled in if (results.Count > 0) { m_workshopItems[m_modId[0]] = results[0]; if (m_modId.Length > 1 && results.Count > 1) { m_workshopItems[m_modId[1]] = results[1]; } m_title = results[0].Title; // Check if the mod owner in the sbmi matches steam owner var owner = results[0].OwnerId; if (m_visibility == null) { m_visibility = (PublishedFileVisibility)(int)results[0].Visibility; } #if SE m_dlcs = results[0].DLCs.ToArray(); #endif m_deps = results[0].Dependencies.ToArray(); MyDebug.AssertDebug(owner == MyGameService.UserId); if (owner != MyGameService.UserId) { MySandboxGame.Log.WriteLineError(string.Format("Owner mismatch! Mod owner: {0}; Current user: {1}", owner, MyGameService.UserId)); MySandboxGame.Log.WriteLineError("Upload/Publish FAILED!"); return(false); } return(true); } return(false); } return(false); }
uint[] GetDLC() { #if SE var results = m_workshopItems?.Values ?? (ICollection <MyWorkshopItem>)WorkshopHelper.GetItemsBlocking(m_modId); if (results?.Count > 0) { if (results.Count > 0) { return(results.First().DLCs.ToArray()); } else { return(null); } } #endif return(null); }
ulong ParseOrGetWorkshopID(string idOrName) { if (ulong.TryParse(idOrName, out var id)) { return(id); } else { // Dependencies can only be mods (not blueprints, scripts, etc) id = WorkshopHelper.GetWorkshopIdFromMod(Path.Combine(WorkshopHelper.GetWorkshopItemPath(m_type), idOrName)).FirstOrDefault().GetId(); // TODO: Better handle if an unpublished mod is passed in, but bailing on exception is fine for now if (id == 0) { throw new ArgumentException($"Cannot determine Workshop ID of dependency '{idOrName}'. Is it a published mod?"); } return(id); } }
public bool UpdatePreviewFileOrTags(ulong modId, MyWorkshopItemPublisher publisher) { ProcessTags(); publisher.Id = modId; publisher.Title = Title; publisher.Visibility = (MyPublishedFileVisibility)(int)(m_visibility ?? GetVisibility()); publisher.Thumbnail = m_previewFilename; publisher.Tags = new List <string>(m_tags); #if SE if (m_dlcs != null) { publisher.DLCs = new HashSet <uint>(m_dlcs); } #else publisher.Folder = m_modPath; #endif if (m_deps != null) { publisher.Dependencies = new List <ulong>(m_deps); } AutoResetEvent resetEvent = new AutoResetEvent(false); try { publisher.ItemPublished += ((result, id) => { if (result == MyGameServiceCallResult.OK) { MySandboxGame.Log.WriteLineAndConsole("Published file update successful"); if (!string.IsNullOrEmpty(m_previewFilename)) { MySandboxGame.Log.WriteLineAndConsole(string.Format("Updated thumbnail: {0}", Title)); } } else { MySandboxGame.Log.WriteLineAndConsole(string.Format("Error during publishing: {0}", (object)result)); } resetEvent.Set(); }); PrintItemDetails(); publisher.Publish(); WorkshopHelper.PublishDependencies(m_modId, m_deps, m_depsToRemove); if (!resetEvent.WaitOne()) { return(false); } } finally { if (resetEvent != null) { resetEvent.Dispose(); } } return(true); }
/// <summary> /// Publishes the mod to the workshop /// </summary> /// <returns></returns> public bool Publish() { bool newMod = false; if (!Directory.Exists(m_modPath)) { MySandboxGame.Log.WriteLineAndConsole(string.Format("Directory does not exist {0}. Wrong option?", m_modPath ?? string.Empty)); return(false); } // Upload/Publish #if SE if (((IMod)this).ModId == 0) #else if (m_modId == 0) #endif { MySandboxGame.Log.WriteLineAndConsole(string.Format("Uploading new {0}: {1}", m_type.ToString(), m_title)); newMod = true; #if SE if (m_modId.Length == 0) { m_modId = new WorkshopId[1] { new WorkshopId(0, MyGameService.GetDefaultUGC().ServiceName) } } ; #endif } else { MySandboxGame.Log.WriteLineAndConsole(string.Format("Updating {0}: {1}; {2}", m_type.ToString(), m_modId.AsString(), m_title)); } // Add the global game filter for file extensions _globalIgnoredExtensions?.ForEach(s => m_ignoredExtensions.Add(s)); // Process Tags ProcessTags(); PrintItemDetails(); MyWorkshopItem[] items = null; if (m_dryrun) { MySandboxGame.Log.WriteLineAndConsole("DRY-RUN; Publish skipped"); return(true); } else { if (_publishMethod != null) { InjectedMethod.ChangeLog = m_changelog; #if SE var result = _publishMethod(m_modPath, m_title, m_description, m_modId, (MyPublishedFileVisibility)(m_visibility ?? PublishedFileVisibility.Private), m_tags, m_ignoredExtensions, m_ignoredPaths, m_dlcs, out items); PublishSuccess = result.Item1 == MyGameServiceCallResult.OK; if (PublishSuccess) { m_modId = items.Select(i => new WorkshopId(i.Id, i.ServiceName)).ToArray(); } #else m_modId = _publishMethod(m_modPath, m_title, m_description, m_modId, (MyPublishedFileVisibility)(m_visibility ?? PublishedFileVisibility.Private), m_tags, m_ignoredExtensions, m_ignoredPaths); #endif } else { MySandboxGame.Log.WriteLineAndConsole(string.Format(Constants.ERROR_Reflection, "PublishItemBlocking")); } // SE libraries don't support updating dependencies, so we have to do that separately WorkshopHelper.PublishDependencies(m_modId, m_deps, m_depsToRemove); } if (((IMod)this).ModId == 0 || !PublishSuccess) { MySandboxGame.Log.WriteLineAndConsole("Upload/Publish FAILED!"); return(false); } else { MySandboxGame.Log.WriteLineAndConsole(string.Format("Upload/Publish success: {0}", m_modId.AsString())); if (newMod) { #if SE if (MyWorkshop.GenerateModInfo(m_modPath, items, MyGameService.UserId)) #else if (MyWorkshop.UpdateModMetadata(m_modPath, m_modId, MySteam.UserId)) #endif { MySandboxGame.Log.WriteLineAndConsole(string.Format("Create modinfo.sbmi success: {0}", m_modId.AsString())); } else { MySandboxGame.Log.WriteLineAndConsole(string.Format("Create modinfo.sbmi FAILED: {0}", m_modId.AsString())); return(false); } } } return(true); } bool FillPropertiesFromPublished() { var results = new List <MyWorkshopItem>(); #if SE if (MyWorkshop.GetItemsBlockingUGC(m_modId.ToList(), results)) #else if (MyWorkshop.GetItemsBlocking(new List <ulong>() { m_modId }, results)) #endif { System.Threading.Thread.Sleep(1000); // Fix for DLC not being filled in if (results.Count > 0) { #if SE m_workshopItems[m_modId[0]] = results[0]; if (m_modId.Length > 1 && results.Count > 1) { m_workshopItems[m_modId[1]] = results[1]; } #else if (results.Count > 1) { m_modId = results[0].Id; } #endif m_title = results[0].Title; // Check if the mod owner in the sbmi matches steam owner var owner = results[0].OwnerId; if (m_visibility == null) { m_visibility = (PublishedFileVisibility)(int)results[0].Visibility; } #if SE m_dlcs = results[0].DLCs.ToArray(); #endif m_deps = results[0].Dependencies.ToArray(); MyDebug.AssertDebug(owner == MyGameService.UserId); if (owner != MyGameService.UserId) { MySandboxGame.Log.WriteLineAndConsole(string.Format("Owner mismatch! Mod owner: {0}; Current user: {1}", owner, MyGameService.UserId)); MySandboxGame.Log.WriteLineAndConsole("Upload/Publish FAILED!"); return(false); } return(true); } return(false); } return(true); } void ProcessTags() { // TODO: This code could be better. // Get the list of existing tags, if there are any var existingTags = GetTags(); var length = m_tags.Length; // Order or tag processing matters // 1) Copy mod type into tags var modtype = m_type.ToString(); // 2) Verify the modtype matches what was listed in the workshop // TODO If type doesn't match, process as workshop type if (existingTags != null && existingTags.Length > 0) { MyDebug.AssertRelease(existingTags.Contains(modtype, StringComparer.InvariantCultureIgnoreCase), string.Format("Mod type '{0}' does not match workshop '{1}'", modtype, existingTags[0])); } #if SE // 3a) check if user passed in the 'development' tag // If so, remove it, and mark the mod as 'dev' so it doesn't get flagged later if (m_tags.Contains(MyWorkshop.WORKSHOP_DEVELOPMENT_TAG)) { m_tags = (from tag in m_tags where tag != MyWorkshop.WORKSHOP_DEVELOPMENT_TAG select tag).ToArray(); m_isDev = true; } #endif // 3b If tags contain mod type, remove it if (m_tags.Contains(modtype, StringComparer.InvariantCultureIgnoreCase)) { m_tags = (from tag in m_tags where string.Compare(tag, modtype, true) != 0 select tag).ToArray(); } // 4) if (m_tags.Length == 1 && m_tags[0] == null && existingTags != null && existingTags.Length > 0) { // 4a) If user passed no tags, use existing ones Array.Resize(ref m_tags, existingTags.Length); Array.Copy(existingTags, m_tags, existingTags.Length); } else { // 4b) Verify passed in tags are valid for this mod type var validTags = new List <MyWorkshop.Category>() { #if SE // 'obsolete' tag is always available, as is 'No Mods' and 'experimental' new MyWorkshop.Category() { Id = "obsolete" }, new MyWorkshop.Category() { Id = "no mods" }, new MyWorkshop.Category() { Id = "experimental" }, #endif }; switch (m_type) { case WorkshopType.Mod: MyWorkshop.ModCategories.ForEach(c => validTags.Add(c)); // Mods have extra tags not in this list #if SE validTags.Add(new MyWorkshop.Category() { Id = "campaign" }); validTags.Add(new MyWorkshop.Category() { Id = "font" }); validTags.Add(new MyWorkshop.Category() { Id = "noscripts" }); #endif break; case WorkshopType.Blueprint: MyWorkshop.BlueprintCategories.ForEach(c => validTags.Add(c)); #if SE // Blueprints have extra tags not in this list validTags.Add(new MyWorkshop.Category() { Id = "large_grid" }); validTags.Add(new MyWorkshop.Category() { Id = "small_grid" }); validTags.Add(new MyWorkshop.Category() { Id = "safe" }); // Mod.io only? #endif break; case WorkshopType.Scenario: MyWorkshop.ScenarioCategories.ForEach(c => validTags.Add(c)); break; case WorkshopType.World: MyWorkshop.WorldCategories.ForEach(c => validTags.Add(c)); break; case WorkshopType.IngameScript: //tags = new MyWorkshop.Category[0]; // There are none currently break; default: MyDebug.FailRelease("Invalid category."); break; } // This query gets all the items in 'm_tags' that do *not* exist in 'validTags' // This is for detecting invalid tags passed in var invalidItems = from utag in m_tags where !( from tag in validTags select tag.Id ).Contains(utag, StringComparer.InvariantCultureIgnoreCase) select utag; if (invalidItems.Count() > 0) { MySandboxGame.Log.WriteLineAndConsole(string.Format("{0} invalid tags: {1}", (m_force ? "Forced" : "Removing"), string.Join(", ", invalidItems))); if (!m_force) { m_tags = (from tag in m_tags where !invalidItems.Contains(tag) select tag).ToArray(); } } // Now prepend the 'Type' tag string[] newTags = new string[m_tags.Length + 1]; newTags[0] = m_type.ToString(); var tags = from tag in validTags select tag.Id; // Convert all tags to proper-case for (var x = 0; x < m_tags.Length; x++) { var tag = m_tags[x]; var newtag = (from vtag in tags where (string.Compare(vtag, tag, true) == 0) select vtag).FirstOrDefault(); if (!string.IsNullOrEmpty(newtag)) { newTags[x + 1] = newtag; } else { newTags[x + 1] = m_tags[x]; } } m_tags = newTags; } #if SE // 5) Set or clear development tag if (m_isDev) { // If user selected dev, add dev tag if (!m_tags.Contains(MyWorkshop.WORKSHOP_DEVELOPMENT_TAG)) { Array.Resize(ref m_tags, m_tags.Length + 1); m_tags[m_tags.Length - 1] = MyWorkshop.WORKSHOP_DEVELOPMENT_TAG; } } else { // If not, remove tag if (m_tags.Contains(MyWorkshop.WORKSHOP_DEVELOPMENT_TAG)) { m_tags = (from tag in m_tags where tag != MyWorkshop.WORKSHOP_DEVELOPMENT_TAG select tag).ToArray(); } } #endif // 6) Strip empty values m_tags = m_tags.Where(x => !string.IsNullOrEmpty(x)).ToArray(); // Done } string[] GetTags() { var results = new List <MyWorkshopItem>(); #if SE if (MyWorkshop.GetItemsBlockingUGC(m_modId.ToList(), results)) #else if (MyWorkshop.GetItemsBlocking(new List <ulong>() { m_modId }, results)) #endif { if (results.Count > 0) { return(results[0].Tags.ToArray()); } else { return(null); } } return(null); } uint[] GetDLC() { #if SE var results = new List <MyWorkshopItem>(); if (MyWorkshop.GetItemsBlockingUGC(m_modId.ToList(), results)) { if (results.Count > 0) { return(results[0].DLCs.ToArray()); } else { return(null); } } #endif return(null); } PublishedFileVisibility GetVisibility() { var results = new List <MyWorkshopItem>(); #if SE if (MyWorkshop.GetItemsBlockingUGC(m_modId.ToList(), results)) #else if (MyWorkshop.GetItemsBlocking(new List <ulong>() { m_modId }, results)) #endif { if (results.Count > 0) { return((PublishedFileVisibility)(int)results[0].Visibility); } else { return(PublishedFileVisibility.Private); } } return(PublishedFileVisibility.Private); }
public bool UpdatePreviewFileOrTags(WorkshopId modId) { MyWorkshopItemPublisher publisher = WorkshopHelper.GetPublisher(modId); var modid = modId.GetId(); publisher.Id = modid; publisher.Title = Title; publisher.Visibility = (MyPublishedFileVisibility)(int)(m_visibility ?? GetVisibility()); publisher.Thumbnail = m_previewFilename; publisher.Tags = new List <string>(m_tags); publisher.Description = m_description; #if SE if (m_dlcs != null) { publisher.DLCs = new HashSet <uint>(m_dlcs); } #else // ME will throw an exception if this isn't set, however setting this in SE // will trigger a full change event (with a new change note entry), which we don't want. publisher.Folder = m_modPath; #endif if (m_deps != null) { publisher.Dependencies = new List <ulong>(m_deps); } AutoResetEvent resetEvent = new AutoResetEvent(false); try { publisher.ItemPublished += ((result, id) => { if (result == MyGameServiceCallResult.OK) { MySandboxGame.Log.WriteLineAndConsole("Published file update successful"); if (!string.IsNullOrEmpty(m_previewFilename)) { MySandboxGame.Log.WriteLineAndConsole(string.Format("Updated thumbnail: {0}", Title)); } } else { MySandboxGame.Log.WriteLineError(string.Format("Error during publishing: {0}", (object)result)); } resetEvent.Set(); }); PrintItemDetails(); if (m_dryrun) { MySandboxGame.Log.WriteLineAndConsole("DRY-RUN; Publish skipped"); return(true); } publisher.Publish(); WorkshopHelper.PublishDependencies(m_modId, m_depsToAdd, m_depsToRemove); if (!resetEvent.WaitOne()) { return(false); } } finally { if (resetEvent != null) { resetEvent.Dispose(); } } return(true); }
public Uploader(WorkshopType type, string path, Options.UploadVerb options, string description = null, string changelog = null) { m_modPath = path; if (ulong.TryParse(m_modPath, out ulong id)) { m_modId = WorkshopIdExtensions.ToWorkshopIds(new[] { id }); } else { m_modId = WorkshopHelper.GetWorkshopIdFromMod(m_modPath); } // Fill defaults before assigning user-defined ones if (m_modId.Length > 0 && m_modId.GetIds()[0] != 0 && !FillPropertiesFromPublished()) { MySandboxGame.Log.WriteLineWarning("Mod has a published ID, but unable to look up properties. This is wrong."); } m_compile = options.Compile; m_dryrun = options.DryRun; // Set visibilty if (options.Visibility != null) { m_visibility = options.Visibility; } else { m_visibility = m_visibility ?? PublishedFileVisibility.Private; // If not already set, set to Private } if (string.IsNullOrEmpty(m_title)) { m_title = Path.GetFileName(path); } m_description = description; m_changelog = changelog; m_type = type; m_force = options.Force; if (options.Thumbnail != null) { m_previewFilename = options.Thumbnail; } var mappedlc = MapDLCStringsToInts(options.DLCs); ProcessDLCs(mappedlc, MapDLCStringsToInts(options.DLCToAdd), MapDLCStringsToInts(options.DLCToRemove)); if (options.Tags != null) { m_tags = options.Tags.ToArray(); } if (options.TagsToAdd != null) { m_tagsToAdd = options.TagsToAdd.ToArray(); } if (options.TagsToRemove != null) { m_tagsToRemove = options.TagsToRemove.ToArray(); } ProcessTags(); ProcessDependencies(options.Dependencies, options.DependenciesToAdd, options.DependenciesToRemove); // This file list should match the PublishXXXAsync methods in MyWorkshop switch (m_type) { case WorkshopType.Mod: m_ignoredPaths.Add("modinfo.sbmi"); break; case WorkshopType.IngameScript: break; case WorkshopType.World: m_ignoredPaths.Add("Backup"); break; case WorkshopType.Blueprint: break; case WorkshopType.Scenario: break; } options.ExcludeExtensions?.Select(s => "." + s.TrimStart(new[] { '.', '*' })).ForEach(s => m_ignoredExtensions.Add(s)); options.IgnorePaths?.ForEach(s => m_ignoredPaths.Add(s)); // Start with the parent file, if it exists. This is at %AppData%\SpaceEngineers\Mods. if (IgnoreFile.TryLoadIgnoreFile(Path.Combine(m_modPath, "..", ".wtignore"), Path.GetFileName(m_modPath), out var extensionsToIgnore, out var pathsToIgnore)) { extensionsToIgnore.ForEach(s => m_ignoredExtensions.Add(s)); pathsToIgnore.ForEach(s => m_ignoredPaths.Add(s)); } if (IgnoreFile.TryLoadIgnoreFile(Path.Combine(m_modPath, ".wtignore"), out extensionsToIgnore, out pathsToIgnore)) { extensionsToIgnore.ForEach(s => m_ignoredExtensions.Add(s)); pathsToIgnore.ForEach(s => m_ignoredPaths.Add(s)); } }
/// <summary> /// Publishes the mod to the workshop /// </summary> /// <returns></returns> public bool Publish() { bool newMod = false; if (!Directory.Exists(m_modPath)) { MySandboxGame.Log.WriteLineWarning(string.Format("Directory does not exist {0}. Wrong option?", m_modPath ?? string.Empty)); return(false); } // Upload/Publish if (((IMod)this).ModId == 0) { MySandboxGame.Log.WriteLineAndConsole(string.Format("Uploading new {0}: {1}", m_type.ToString(), m_title)); newMod = true; if (m_modId.Length == 0) { m_modId = WorkshopIdExtensions.ToWorkshopIds(new ulong[] { 0 }); } } else { MySandboxGame.Log.WriteLineAndConsole(string.Format("Updating {0}: {1}; {2}", m_type.ToString(), m_modId.AsString(), m_title)); } // Add the global game filter for file extensions WorkshopHelper.IgnoredExtensions?.ForEach(s => m_ignoredExtensions.Add(s)); PrintItemDetails(); MyWorkshopItem[] items = null; if (m_dryrun) { MySandboxGame.Log.WriteLineAndConsole("DRY-RUN; Publish skipped"); return(true); } else { InjectedMethod.ChangeLog = m_changelog; if (WorkshopHelper.PublishItemBlocking(m_modPath, m_title, m_description, m_modId, (MyPublishedFileVisibility)m_visibility, m_tags, m_ignoredExtensions, m_ignoredPaths, m_dlcs, out items)) { m_modId = items.ToWorkshopIds(); } else { MySandboxGame.Log.WriteLineError(string.Format(Constants.ERROR_Reflection, "PublishItemBlocking")); } // SE libraries don't support updating dependencies, so we have to do that separately WorkshopHelper.PublishDependencies(m_modId, m_depsToAdd, m_depsToRemove); // SE also doesn't support removing DLC items, so do that too WorkshopHelper.PublishDLC(m_modId, m_dlcsToAdd, m_dlcsToRemove); } if (((IMod)this).ModId == 0 || !WorkshopHelper.PublishSuccess) { MySandboxGame.Log.WriteLineError("Upload/Publish FAILED!"); return(false); } else { MySandboxGame.Log.WriteLineAndConsole(string.Format("Upload/Publish success: {0}", m_modId.AsString())); if (newMod) { if (WorkshopHelper.GenerateModInfo(m_modPath, items, m_modId, MyGameService.UserId)) { MySandboxGame.Log.WriteLineAndConsole(string.Format("Create modinfo.sbmi success: {0}", m_modId.AsString())); } else { MySandboxGame.Log.WriteLineAndConsole(string.Format("Create modinfo.sbmi FAILED: {0}", m_modId.AsString())); return(false); } } } return(true); }
/// <summary> /// Compiles the mod /// </summary> /// <returns></returns> public bool Compile() { // Compile if (m_compile) { if (m_type == WorkshopType.Mod) { MySandboxGame.Log.WriteLineAndConsole("Compiling..."); var mod = WorkshopHelper.GetContext(m_modPath, m_workshopItems.GetValueOrDefault(m_modId.FirstOrDefault()), m_modId, m_title); if (WorkshopHelper.LoadScripts(m_modPath, mod)) { // Process any errors var errors = WorkshopHelper.GetErrors(); if (errors.Count > 0) { int errorCount = 0; int warningCount = 0; // This is not efficient, but I'm lazy foreach (var error in errors) { if (error.Severity >= TErrorSeverity.Error) { errorCount++; } if (error.Severity == TErrorSeverity.Warning) { warningCount++; } } if (errorCount > 0) { MySandboxGame.Log.WriteLineError(string.Format("There are {0} compile errors:", errorCount)); } if (warningCount > 0) { MySandboxGame.Log.WriteLineWarning(string.Format("There are {0} compile warnings:", warningCount)); } // Output raw message, which is usually in msbuild friendly format, for automated tools foreach (var error in errors) { var color = error.Severity == TErrorSeverity.Warning ? ConsoleColor.Yellow : ConsoleColor.Red; ProgramBase.ConsoleWriteColored(color, () => System.Console.Error.WriteLine(error.GetErrorText())); } WorkshopHelper.ClearErrors(); if (errorCount > 0) { MySandboxGame.Log.WriteLineError("Compilation FAILED!"); return(false); } } MySandboxGame.Log.WriteLineAndConsole("Compilation successful!"); } } #if SE else if (m_type == WorkshopType.IngameScript) { MySandboxGame.Log.WriteLineAndConsole("Compiling..."); // Load the ingame script from the disk // I don't like this, but meh var input = new StreamReader(Path.Combine(m_modPath, "Script.cs")); var program = input.ReadToEnd(); input.Close(); var scripts = new List <Script>(); scripts.Add(MyScriptCompiler.Static.GetIngameScript(program, "Program", typeof(Sandbox.ModAPI.Ingame.MyGridProgram).Name, "sealed partial")); var messages = new List <Message>(); var assembly = MyVRage.Platform.Scripting.CompileIngameScriptAsync(Path.Combine(VRage.FileSystem.MyFileSystem.UserDataPath, "SEWT-Script - " + Path.GetFileName(m_modPath)), program, out messages, "SEWT Compiled PB Script", "Program", typeof(Sandbox.ModAPI.Ingame.MyGridProgram).Name).Result; if (messages.Count > 0) { MySandboxGame.Log.WriteLineAndConsole(string.Format("There are {0} compile messages:", messages.Count)); int errors = 0; foreach (var msg in messages) { var color = msg.IsError ? ConsoleColor.Red : ConsoleColor.Gray; ProgramBase.ConsoleWriteColored(color, () => MySandboxGame.Log.WriteLineAndConsole(msg.Text)); if (msg.IsError) { errors++; } } if (errors > 0) { MySandboxGame.Log.WriteLineError("Compilation FAILED!"); return(false); } } if (assembly == null) { // How can this happen? MySandboxGame.Log.WriteLineError("Compilation FAILED!"); return(false); } MySandboxGame.Log.WriteLineAndConsole("Compilation successful!"); } #endif return(true); } return(true); }
static bool ProcessItemsDownload(WorkshopType type, string[] paths, Options options) { if (paths == null) { return(true); } var width = Console.IsOutputRedirected ? 256 : Console.WindowWidth; var items = new List <MyWorkshopItem>(); var modids = paths.Select(ulong.Parse); MySandboxGame.Log.WriteLineAndConsole(string.Format("Processing {0}s...", type.ToString())); var downloadPath = WorkshopHelper.GetWorkshopItemPath(type); #if SE var workshopIds = new List <VRage.Game.WorkshopId>(); foreach (var id in modids) { workshopIds.Add(new VRage.Game.WorkshopId(id, MyGameService.GetDefaultUGC().ServiceName)); } if (MyWorkshop.GetItemsBlockingUGC(workshopIds, items)) #else if (MyWorkshop.GetItemsBlocking(modids, items)) #endif { System.Threading.Thread.Sleep(1000); // Fix for DLC not being filled in bool success = false; if (type == WorkshopType.Mod) { #if SE var result = MyWorkshop.DownloadModsBlockingUGC(items, null); #else var result = MyWorkshop.DownloadModsBlocking(items, null); #endif success = result.Success; } else { if (type == WorkshopType.Blueprint) { var loopsuccess = false; foreach (var item in items) { #if SE loopsuccess = MyWorkshop.DownloadBlueprintBlockingUGC(item); #else loopsuccess = MyWorkshop.DownloadBlueprintBlocking(item, null); #endif if (!loopsuccess) { MySandboxGame.Log.WriteLineAndConsole(string.Format("Download of {0} FAILED!", item.Id)); } else { success = true; } } } #if SE else if (type == WorkshopType.IngameScript) { var loopsuccess = false; foreach (var item in items) { loopsuccess = MyWorkshop.DownloadScriptBlocking(item); if (!loopsuccess) { MySandboxGame.Log.WriteLineAndConsole(string.Format("Download of {0} FAILED!", item.Id)); } else { success = true; } } } #endif #if SE else if (type == WorkshopType.World || type == WorkshopType.Scenario) { var loopsuccess = false; string path; MyWorkshop.MyWorkshopPathInfo pathinfo = type == WorkshopType.World ? MyWorkshop.MyWorkshopPathInfo.CreateWorldInfo() : MyWorkshop.MyWorkshopPathInfo.CreateScenarioInfo(); foreach (var item in items) { // This downloads and extracts automatically, no control over it loopsuccess = MyWorkshop.TryCreateWorldInstanceBlocking(item, pathinfo, out path, false); if (!loopsuccess) { MySandboxGame.Log.WriteLineAndConsole(string.Format("Download of {0} FAILED!", item.Id)); } else { MySandboxGame.Log.WriteLineAndConsole(string.Format("Downloaded '{0}' to {1}", item.Title, path)); success = true; } } } #endif else { throw new NotSupportedException(string.Format("Downloading of {0} not yet supported.", type.ToString())); } } if (success) { MySandboxGame.Log.WriteLineAndConsole("Download success!"); } else { MySandboxGame.Log.WriteLineAndConsole("Download FAILED!"); return(false); } foreach (var item in items) { MySandboxGame.Log.WriteLineAndConsole(string.Format("Downloading mod: {0}; {1}", item.Id, item.Title)); MySandboxGame.Log.WriteLineAndConsole(string.Format("Visibility: {0}", item.Visibility)); MySandboxGame.Log.WriteLineAndConsole(string.Format("Tags: {0}", string.Join(", ", string.Join(", ", item.Tags)))); #if SE MySandboxGame.Log.WriteLineAndConsole(string.Format("DLC requirements: {0}", (item.DLCs.Count > 0 ? string.Join(", ", item.DLCs.Select(i => { try { return(Sandbox.Game.MyDLCs.DLCs[i].Name); } catch { return($"Unknown({i})"); } })) : "None"))); #endif MySandboxGame.Log.WriteLineAndConsole(string.Format("Dependencies: {0}", (item.Dependencies.Count > 0 ? string.Empty : "None"))); if (item.Dependencies.Count > 0) { List <MyWorkshopItem> depItems = new List <MyWorkshopItem>(); #if SE workshopIds.Clear(); foreach (var id in item.Dependencies) { workshopIds.Add(new VRage.Game.WorkshopId(id, MyGameService.GetDefaultUGC().ServiceName)); } if (MyWorkshop.GetItemsBlockingUGC(workshopIds, depItems)) #else if (MyWorkshop.GetItemsBlocking(item.Dependencies, depItems)) #endif { depItems.ForEach(i => MySandboxGame.Log.WriteLineAndConsole(string.Format("{0,15} -> {1}", i.Id, i.Title.Substring(0, Math.Min(i.Title.Length, width - 45))))); } else { MySandboxGame.Log.WriteLineAndConsole(string.Format(" {0}", string.Join(", ", item.Dependencies))); } } MySandboxGame.Log.WriteLineAndConsole(string.Format("Location: {0}", item.Folder)); if (options.Extract) { var mod = new Downloader(downloadPath, item); mod.Extract(); } MySandboxGame.Log.WriteLineAndConsole(string.Empty); } } return(true); }