/// <summary> /// Replaces a method with another one /// </summary> /// <param name="sourceType">Original type</param> /// <param name="sourceMethod">Original method name</param> /// <param name="destinationType">New type</param> /// <param name="destinationMethod">New method name</param> void ReplaceMethod(Type sourceType, string sourceMethod, BindingFlags sourceBinding, Type destinationType, string destinationMethod, BindingFlags?destinationBinding = null, Type[] types = null) { ParameterInfo[] sourceParameters; ParameterInfo[] destinationParameters; MethodInfo methodtoreplace = null; if (types == null) { methodtoreplace = sourceType.GetMethod(sourceMethod, sourceBinding); } else { methodtoreplace = sourceType.GetMethod(sourceMethod, types); } var methodtoinject = destinationType.GetMethod(destinationMethod, destinationBinding ?? sourceBinding); MyDebug.AssertRelease(methodtoreplace != null); if (methodtoreplace != null && methodtoinject != null) { sourceParameters = methodtoreplace.GetParameters(); destinationParameters = methodtoinject.GetParameters(); MyDebug.AssertDebug(sourceParameters.Length == destinationParameters.Length); bool valid = true; // Verify signatures for (var x = 0; x < Math.Min(destinationParameters.Length, sourceParameters.Length); x++) { MyDebug.AssertDebug(destinationParameters[x].ParameterType == sourceParameters[x].ParameterType); if (destinationParameters[x].ParameterType != sourceParameters[x].ParameterType) { valid = false; } } if (sourceParameters.Length != destinationParameters.Length || !valid) { methodtoreplace = null; } } if (methodtoreplace != null && methodtoinject != null) { MethodUtil.ReplaceMethod(methodtoreplace, methodtoinject); } else { MySandboxGame.Log.WriteLineAndConsole(string.Format(Constants.ERROR_Reflection, sourceMethod)); } }
/// <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); }
private void SetupReflection() { if (m_compile && m_type == WorkshopType.Mod) { #if SE if (_scriptManager == null) { _scriptManager = new MyScriptManager(); } #else if (_scriptManager == null) { _scriptManager = new MyModManager(); } #endif if (_compileMethod == null) { var compileMethod = _scriptManager.GetType().GetMethod("LoadScripts", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null #if SE , new[] { typeof(string), typeof(MyModContext) } #else , new[] { typeof(MyModContext) } #endif , null); MyDebug.AssertDebug(compileMethod != null); if (compileMethod != null) { _compileMethod = Delegate.CreateDelegate(typeof(LoadScripts), _scriptManager, compileMethod, false) as LoadScripts; } if (_compileMethod == null) { MySandboxGame.Log.WriteLineAndConsole(string.Format(Constants.ERROR_Reflection, "LoadScripts")); } } } var publishMethod = typeof(MyWorkshop).GetMethod("PublishItemBlocking", BindingFlags.Static | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(string), typeof(string), typeof(string), m_modId.GetType(), typeof(MyPublishedFileVisibility), typeof(string[]), typeof(HashSet <string>), typeof(HashSet <string>), #if SE typeof(uint[]), typeof(MyWorkshopItem[]).MakeByRefType() #endif }, null); MyDebug.AssertDebug(publishMethod != null); if (publishMethod != null) { _publishMethod = Delegate.CreateDelegate(typeof(PublishItemBlocking), publishMethod, false) as PublishItemBlocking; } if (_publishMethod == null) { MySandboxGame.Log.WriteLineAndConsole(string.Format(Constants.ERROR_Reflection, "PublishItemBlocking")); } if (_globalIgnoredExtensions == null) { _globalIgnoredExtensions = (HashSet <string>) typeof(MyWorkshop).GetField("m_ignoredExecutableExtensions", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null); } if (_previewFileNames == null) { _previewFileNames = (string[])typeof(MyWorkshop).GetField("m_previewFileNames", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null); } if (_previewFileNames == null) { _previewFileNames = new string[] { "thumb.png", "thumb.jpg" } } ; #if !SE try { if (__refget_m_publishSuccess == null) { __refget_m_publishSuccess = MethodUtil.create_refgetter <MyWorkshop, bool>("m_publishSuccess", BindingFlags.NonPublic | BindingFlags.Static); } } catch (Exception ex) { MySandboxGame.Log.WriteLineAndConsole(string.Format(Constants.ERROR_Reflection, "m_publishSuccess")); MySandboxGame.Log.WriteLine(ex.Message); } #endif }