Difference between revisions of "User:FallenAvatar/Code Based Mod"
FallenAvatar (Talk | contribs) m |
FallenAvatar (Talk | contribs) (RFC: Replace current Code Based Mod page with this) |
||
Line 97: | Line 97: | ||
== Setting up Debugging == | == Setting up Debugging == | ||
This section is technically optional, but it will make your life much, ''much'', '''much''' easier in the long run. | This section is technically optional, but it will make your life much, ''much'', '''much''' easier in the long run. | ||
+ | * Right click the project and select properties | ||
+ | * Select the "Build Events" tab on the left. | ||
+ | ** ''Replace "C:\Program Files (x86)\Steam\steamapps\common\Staxel\" with the path for where Staxel is installed for you.'' | ||
+ | * In the "Pre-build event command line" section add: (Taken from [https://stackoverflow.com/a/4453285/36965 here] and modified | ||
+ | <pre> | ||
+ | echo namespace $(ProjectName) > "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo { >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo ///^<summary^>Info about this binary and the solution that was used to build it.^</summary^> >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo public static class SolutionInfo >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo { >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo ///^<summary^>The name of the solution^</summary^> >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo public const string SolutionName = "$(SolutionName)"; >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo ///^<summary^>The name of the solution file^</summary^> >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo public const string SolutionFileName = "$(SolutionFileName)"; >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo ///^<summary^>The name of the project^</summary^> >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo public const string ProjectName = "$(ProjectName)"; >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo } >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | echo } >> "$(ProjectDir)\SolutionInfo.cs" | ||
+ | </pre> | ||
+ | * In the "Post-build event command line" section add: | ||
+ | <pre> | ||
+ | copy "$(TargetDir)$(TargetName).*" "C:\Program Files (x86)\Steam\steamapps\common\Staxel\bin\" | ||
+ | </pre> | ||
+ | * Select the "Debug" tab on the left | ||
+ | ** Set "Start external program" to "C:\Program Files (x86)\Steam\steamapps\common\Staxel\bin\Staxel.Client.exe" | ||
+ | ** Set "Working directory" to "C:\Program Files (x86)\Steam\steamapps\common\Staxel\bin\" | ||
+ | |||
+ | '' This is enough to get started, you will have to start a game in Staxel, switch back to Visual Studio and Debug > Attach to Process and select "Staxel.Server.NoConsole.exe" to be able to do proper debugging, but it will work '' | ||
+ | |||
+ | ''Or you can also complete the following steps'' | ||
+ | * The following code was originally found [https://gist.github.com/atruskie/3813175 here] | ||
+ | * Add a new file to your project called "VisualStudioAttacher.cs" | ||
+ | * Paste the following contents into the file | ||
+ | <pre> | ||
+ | /// <summary> | ||
+ | /// I found this gem originally on PasteBin (http://pastebin.com/pHWMP3EQ) and (http://pastebin.com/KKyBpWQW). | ||
+ | /// Since it was dropped there anonymously, it had little documentation, and I made a change, I thought I'd migrate it to a gist. | ||
+ | /// I found this snippet of code extremely useful, it flows much better than the alternative Debugger.Launch() call. | ||
+ | /// | ||
+ | /// Example usage: | ||
+ | /// /// <summary> | ||
+ | /// /// Attaches a debugger, if built in with DEBUG symbol | ||
+ | /// /// </summary> | ||
+ | /// [Conditional("DEBUG")] | ||
+ | /// private static void AttachDebugger() | ||
+ | /// { | ||
+ | /// if (!Debugger.IsAttached) | ||
+ | /// { | ||
+ | /// // do a search for any Visual Studio processes that also have our solution currently open | ||
+ | /// var vsProcess = | ||
+ | /// VisualStudioAttacher.GetVisualStudioForSolutions( | ||
+ | /// new List<string>() { "FooBar2012.sln", "FooBar.sln" }); | ||
+ | /// | ||
+ | /// if (vsProcess != null) | ||
+ | /// { | ||
+ | /// VisualStudioAttacher.AttachVisualStudioToProcess(proc, Process.GetCurrentProcess()); | ||
+ | /// } | ||
+ | /// else | ||
+ | /// { | ||
+ | /// // try and attach the old fashioned way | ||
+ | /// Debugger.Launch(); | ||
+ | /// } | ||
+ | /// | ||
+ | /// if (Debugger.IsAttached) | ||
+ | /// { | ||
+ | /// Console.WriteLine("\t>>> Attach sucessful"); | ||
+ | /// } | ||
+ | /// } | ||
+ | /// } | ||
+ | /// </summary> | ||
+ | using System; | ||
+ | using System.Collections.Generic; | ||
+ | using System.IO; | ||
+ | using System.Linq; | ||
+ | using System.Runtime.InteropServices; | ||
+ | using System.Runtime.InteropServices.ComTypes; | ||
+ | using EnvDTE; | ||
+ | using DTEProcess = EnvDTE.Process; | ||
+ | using Process = System.Diagnostics.Process; | ||
+ | |||
+ | namespace Test471.Debugging { | ||
+ | #region Classes | ||
+ | |||
+ | public static class VisualStudioAttacher { | ||
+ | #region Public Methods | ||
+ | |||
+ | [DllImport( "User32" )] | ||
+ | private static extern int ShowWindow( int hwnd, int nCmdShow ); | ||
+ | |||
+ | [DllImport( "ole32.dll" )] | ||
+ | public static extern int CreateBindCtx( int reserved, out IBindCtx ppbc ); | ||
+ | |||
+ | [DllImport( "ole32.dll" )] | ||
+ | public static extern int GetRunningObjectTable( int reserved, out IRunningObjectTable prot ); | ||
+ | |||
+ | |||
+ | [DllImport( "user32.dll", CharSet = CharSet.Auto, SetLastError = true )] | ||
+ | public static extern bool SetForegroundWindow( IntPtr hWnd ); | ||
+ | [DllImport( "user32.dll", CharSet = CharSet.Auto, SetLastError = true )] | ||
+ | public static extern IntPtr SetFocus( IntPtr hWnd ); | ||
+ | |||
+ | |||
+ | public static string GetSolutionForVisualStudio( Process visualStudioProcess ) { | ||
+ | _DTE visualStudioInstance; | ||
+ | if( TryGetVsInstance( visualStudioProcess.Id, out visualStudioInstance ) ) { | ||
+ | try { | ||
+ | return visualStudioInstance.Solution.FullName; | ||
+ | } catch( Exception ) { | ||
+ | } | ||
+ | } | ||
+ | return null; | ||
+ | } | ||
+ | |||
+ | public static Process GetAttachedVisualStudio( Process applicationProcess ) { | ||
+ | IEnumerable<Process> visualStudios = GetVisualStudioProcesses(); | ||
+ | |||
+ | foreach( Process visualStudio in visualStudios ) { | ||
+ | _DTE visualStudioInstance; | ||
+ | if( TryGetVsInstance( visualStudio.Id, out visualStudioInstance ) ) { | ||
+ | try { | ||
+ | foreach( Process debuggedProcess in visualStudioInstance.Debugger.DebuggedProcesses ) { | ||
+ | if( debuggedProcess.Id == applicationProcess.Id ) { | ||
+ | return debuggedProcess; | ||
+ | } | ||
+ | } | ||
+ | } catch( Exception ) { | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | return null; | ||
+ | } | ||
+ | |||
+ | public static void AttachVisualStudioToProcess( Process visualStudioProcess, Process applicationProcess ) { | ||
+ | _DTE visualStudioInstance; | ||
+ | |||
+ | if( TryGetVsInstance( visualStudioProcess.Id, out visualStudioInstance ) ) { | ||
+ | //Find the process you want the VS instance to attach to... | ||
+ | DTEProcess processToAttachTo = visualStudioInstance.Debugger.LocalProcesses.Cast<DTEProcess>().FirstOrDefault( process => process.ProcessID == applicationProcess.Id ); | ||
+ | |||
+ | //Attach to the process. | ||
+ | if( processToAttachTo != null ) { | ||
+ | processToAttachTo.Attach(); | ||
+ | |||
+ | ShowWindow( (int)visualStudioProcess.MainWindowHandle, 3 ); | ||
+ | SetForegroundWindow( visualStudioProcess.MainWindowHandle ); | ||
+ | } else { | ||
+ | throw new InvalidOperationException( "Visual Studio process cannot find specified application '" + applicationProcess.Id + "'" ); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public static Process GetVisualStudioForSolutions( List<string> solutionNames ) { | ||
+ | foreach( string solution in solutionNames ) { | ||
+ | Process visualStudioForSolution = GetVisualStudioForSolution( solution ); | ||
+ | if( visualStudioForSolution != null ) { | ||
+ | return visualStudioForSolution; ; | ||
+ | } | ||
+ | } | ||
+ | return null; | ||
+ | } | ||
+ | |||
+ | |||
+ | public static Process GetVisualStudioForSolution( string solutionName ) { | ||
+ | IEnumerable<Process> visualStudios = GetVisualStudioProcesses(); | ||
+ | |||
+ | foreach( Process visualStudio in visualStudios ) { | ||
+ | _DTE visualStudioInstance; | ||
+ | if( TryGetVsInstance( visualStudio.Id, out visualStudioInstance ) ) { | ||
+ | try { | ||
+ | string actualSolutionName = Path.GetFileName( visualStudioInstance.Solution.FullName ); | ||
+ | |||
+ | if( string.Compare( actualSolutionName, solutionName, StringComparison.InvariantCultureIgnoreCase ) == 0 ) { | ||
+ | return visualStudio; | ||
+ | } | ||
+ | } catch( Exception ) { | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | return null; | ||
+ | } | ||
+ | |||
+ | #endregion | ||
+ | |||
+ | #region Private Methods | ||
+ | |||
+ | private static IEnumerable<Process> GetVisualStudioProcesses() { | ||
+ | Process[] processes = Process.GetProcesses(); | ||
+ | return processes.Where( o => o.ProcessName.Contains( "devenv" ) ); | ||
+ | } | ||
+ | |||
+ | private static bool TryGetVsInstance( int processId, out _DTE instance ) { | ||
+ | IntPtr numFetched = IntPtr.Zero; | ||
+ | IRunningObjectTable runningObjectTable; | ||
+ | IEnumMoniker monikerEnumerator; | ||
+ | IMoniker[] monikers = new IMoniker[1]; | ||
+ | |||
+ | GetRunningObjectTable( 0, out runningObjectTable ); | ||
+ | runningObjectTable.EnumRunning( out monikerEnumerator ); | ||
+ | monikerEnumerator.Reset(); | ||
+ | |||
+ | while( monikerEnumerator.Next( 1, monikers, numFetched ) == 0 ) { | ||
+ | IBindCtx ctx; | ||
+ | CreateBindCtx( 0, out ctx ); | ||
+ | |||
+ | string runningObjectName; | ||
+ | monikers[0].GetDisplayName( ctx, null, out runningObjectName ); | ||
+ | |||
+ | object runningObjectVal; | ||
+ | runningObjectTable.GetObject( monikers[0], out runningObjectVal ); | ||
+ | |||
+ | if( runningObjectVal is _DTE && runningObjectName.StartsWith( "!VisualStudio" ) ) { | ||
+ | int currentProcessId = int.Parse( runningObjectName.Split( ':' )[1] ); | ||
+ | |||
+ | if( currentProcessId == processId ) { | ||
+ | instance = (_DTE)runningObjectVal; | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | instance = null; | ||
+ | return false; | ||
+ | } | ||
+ | |||
+ | #endregion | ||
+ | } | ||
+ | |||
+ | #endregion | ||
+ | } | ||
+ | </pre> | ||
+ | * Open Mod.cs and add a function | ||
+ | <pre> | ||
+ | [Conditional("DEBUG")] | ||
+ | private void AttachDebugger() { | ||
+ | // do a search for any Visual Studio processes that also have our solution currently open | ||
+ | var vsProcess = Debugging.VisualStudioAttacher.GetVisualStudioForSolutions( new List<string>() { SolutionInfo.SolutionFileName } ); | ||
+ | |||
+ | if( vsProcess != null ) { | ||
+ | Debugging.VisualStudioAttacher.AttachVisualStudioToProcess( vsProcess, Process.GetCurrentProcess() ); | ||
+ | } else { | ||
+ | // try and attach the old fashioned way | ||
+ | Debugger.Launch(); | ||
+ | } | ||
+ | |||
+ | if( Debugger.IsAttached ) { | ||
+ | Logger.WriteLine( "[mod] Test472 :: Attched to VisualStudio for debugging" ); | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | * Add to the function "GameContextInitializeInit" | ||
+ | <pre>this.AttachDebugger();</pre> | ||
+ | * Add a reference to the project to "C:\Program Files (x86)\Common Files\Microsoft Shared\MSEnv\PublicAssemblies\envdte.dll" | ||
+ | |||
+ | '' You should now be able to simply hit F5 to run your project and have full debugging capabilities. | ||
== Interface Documentation == | == Interface Documentation == |
Revision as of 22:05, 2 February 2018
[[Category:Modding]] Staxel also supports adding in your own code which can attach itself to interfaces within Staxel's code. This attachment is fairly rudimentary, and isn't tested as much as the other features. However, if you've ever wanted to add new features to Staxel, this is the way to do it.
Contents
Creating The Project
This page assumes that you are running Visual Studio 2017. If you run other versions of Visual Studio, locations of things may have changed. Other IDE's will definitely have different methods for some actions.
You should not need any external dependencies to develop Mods for Staxel. However, complex changes to the game may need XNA. In that case you can find what you need here: MXA
To create a new Code Based Mod:
- Open Visual Studio
- Create a New Project (File > New > Project)
- Select a Class Library (Installed > Visual C# > Windows Classic Desktop > Class Library (.NET Framework))
- Note You should be able to use your .Net Language of choice, but all sample code is in C#.
- You may name it how you like, and use the newest version of the framework.
- Tested with up to 4.7.1
- Pick where you want your mod saved
- Click Ok
Setting Up The Project
Now the project needs to be setup to recognize Staxel's code and build itself correctly for debugging.
- Add a Reference by Right-Clicking the Project and selecting Add > Reference
- Click Browse at the bottom right and navigate to where Staxel is installed, and open the bin folder.
- For Staxel on Steam on Windows, this is likely "C:\Program Files (x86)\Steam\steamapps\common\Staxel\bin"
- Hold Ctrl (Control) and select:
- Plukit.Base.dll
- Staxel.dll
- Click Add
- Click Ok
- Click Browse at the bottom right and navigate to where Staxel is installed, and open the bin folder.
- Add a new Text File and name it StaxelMod.mod
- Replace StaxelMod with what you named your project
- Simply add "{}" without the quotation marks to the file and save it.
- This file is a config file. However nothing is supported in it yet. In the future, this might change.
- Right Click this file in the Solution Explorer and select properties. Change "Copy to Output Directory" to "Copy if newer"
- Rename the Class1 class to Mod and allow Visual Studio to rename all references to it.
- Open Mod.cs and replacing "StaxelMod" as necessary, overwrite its content with:
using Plukit.Base; using Staxel.Items; using Staxel.Logic; using Staxel.Tiles; namespace StaxelMod { public class Mod : Staxel.Modding.IModHookV2 { #region Loading public void GameContextInitializeInit() {} public void ClientContextInitializeInit() {} public void ClientContextInitializeBefore() {} public void GameContextInitializeBefore() {} public void GameContextInitializeAfter() {} public void ClientContextInitializeAfter() {} #endregion #region Running #region Every From public void UniverseUpdateAfter() {} public void UniverseUpdateBefore( Universe universe, Timestep step ) {} #endregion public bool CanPlaceTile( Entity entity, Vector3I location, Tile tile, TileAccessFlags accessFlags ) { return true; } public bool CanRemoveTile( Entity entity, Vector3I location, TileAccessFlags accessFlags ) { return true; } public bool CanReplaceTile( Entity entity, Vector3I location, Tile tile, TileAccessFlags accessFlags ) { return true; } #endregion #region Closing public void CleanupOldSession() { } public void GameContextDeinitialize() { } public void ClientContextDeinitialize() { } public void Dispose() { } #endregion #region Reloading public void ClientContextReloadBefore() { } public void GameContextReloadBefore() { } public void GameContextReloadAfter() { } public void ClientContextReloadAfter() { } #endregion } }
Note: Currently ClientContextDeinitialize is called during initialization intead of ClientContextInitializeInit
The project is now able to be built, copied to the Staxel bin directory and loaded as a Mod, of course it doesn't do much at the moment
Setting up Debugging
This section is technically optional, but it will make your life much, much, much easier in the long run.
- Right click the project and select properties
- Select the "Build Events" tab on the left.
- Replace "C:\Program Files (x86)\Steam\steamapps\common\Staxel\" with the path for where Staxel is installed for you.
- In the "Pre-build event command line" section add: (Taken from here and modified
echo namespace $(ProjectName) > "$(ProjectDir)\SolutionInfo.cs" echo { >> "$(ProjectDir)\SolutionInfo.cs" echo ///^<summary^>Info about this binary and the solution that was used to build it.^</summary^> >> "$(ProjectDir)\SolutionInfo.cs" echo public static class SolutionInfo >> "$(ProjectDir)\SolutionInfo.cs" echo { >> "$(ProjectDir)\SolutionInfo.cs" echo ///^<summary^>The name of the solution^</summary^> >> "$(ProjectDir)\SolutionInfo.cs" echo public const string SolutionName = "$(SolutionName)"; >> "$(ProjectDir)\SolutionInfo.cs" echo ///^<summary^>The name of the solution file^</summary^> >> "$(ProjectDir)\SolutionInfo.cs" echo public const string SolutionFileName = "$(SolutionFileName)"; >> "$(ProjectDir)\SolutionInfo.cs" echo ///^<summary^>The name of the project^</summary^> >> "$(ProjectDir)\SolutionInfo.cs" echo public const string ProjectName = "$(ProjectName)"; >> "$(ProjectDir)\SolutionInfo.cs" echo } >> "$(ProjectDir)\SolutionInfo.cs" echo } >> "$(ProjectDir)\SolutionInfo.cs"
- In the "Post-build event command line" section add:
copy "$(TargetDir)$(TargetName).*" "C:\Program Files (x86)\Steam\steamapps\common\Staxel\bin\"
- Select the "Debug" tab on the left
- Set "Start external program" to "C:\Program Files (x86)\Steam\steamapps\common\Staxel\bin\Staxel.Client.exe"
- Set "Working directory" to "C:\Program Files (x86)\Steam\steamapps\common\Staxel\bin\"
This is enough to get started, you will have to start a game in Staxel, switch back to Visual Studio and Debug > Attach to Process and select "Staxel.Server.NoConsole.exe" to be able to do proper debugging, but it will work
Or you can also complete the following steps
- The following code was originally found here
- Add a new file to your project called "VisualStudioAttacher.cs"
- Paste the following contents into the file
/// <summary> /// I found this gem originally on PasteBin (http://pastebin.com/pHWMP3EQ) and (http://pastebin.com/KKyBpWQW). /// Since it was dropped there anonymously, it had little documentation, and I made a change, I thought I'd migrate it to a gist. /// I found this snippet of code extremely useful, it flows much better than the alternative Debugger.Launch() call. /// /// Example usage: /// /// <summary> /// /// Attaches a debugger, if built in with DEBUG symbol /// /// </summary> /// [Conditional("DEBUG")] /// private static void AttachDebugger() /// { /// if (!Debugger.IsAttached) /// { /// // do a search for any Visual Studio processes that also have our solution currently open /// var vsProcess = /// VisualStudioAttacher.GetVisualStudioForSolutions( /// new List<string>() { "FooBar2012.sln", "FooBar.sln" }); /// /// if (vsProcess != null) /// { /// VisualStudioAttacher.AttachVisualStudioToProcess(proc, Process.GetCurrentProcess()); /// } /// else /// { /// // try and attach the old fashioned way /// Debugger.Launch(); /// } /// /// if (Debugger.IsAttached) /// { /// Console.WriteLine("\t>>> Attach sucessful"); /// } /// } /// } /// </summary> using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using EnvDTE; using DTEProcess = EnvDTE.Process; using Process = System.Diagnostics.Process; namespace Test471.Debugging { #region Classes public static class VisualStudioAttacher { #region Public Methods [DllImport( "User32" )] private static extern int ShowWindow( int hwnd, int nCmdShow ); [DllImport( "ole32.dll" )] public static extern int CreateBindCtx( int reserved, out IBindCtx ppbc ); [DllImport( "ole32.dll" )] public static extern int GetRunningObjectTable( int reserved, out IRunningObjectTable prot ); [DllImport( "user32.dll", CharSet = CharSet.Auto, SetLastError = true )] public static extern bool SetForegroundWindow( IntPtr hWnd ); [DllImport( "user32.dll", CharSet = CharSet.Auto, SetLastError = true )] public static extern IntPtr SetFocus( IntPtr hWnd ); public static string GetSolutionForVisualStudio( Process visualStudioProcess ) { _DTE visualStudioInstance; if( TryGetVsInstance( visualStudioProcess.Id, out visualStudioInstance ) ) { try { return visualStudioInstance.Solution.FullName; } catch( Exception ) { } } return null; } public static Process GetAttachedVisualStudio( Process applicationProcess ) { IEnumerable<Process> visualStudios = GetVisualStudioProcesses(); foreach( Process visualStudio in visualStudios ) { _DTE visualStudioInstance; if( TryGetVsInstance( visualStudio.Id, out visualStudioInstance ) ) { try { foreach( Process debuggedProcess in visualStudioInstance.Debugger.DebuggedProcesses ) { if( debuggedProcess.Id == applicationProcess.Id ) { return debuggedProcess; } } } catch( Exception ) { } } } return null; } public static void AttachVisualStudioToProcess( Process visualStudioProcess, Process applicationProcess ) { _DTE visualStudioInstance; if( TryGetVsInstance( visualStudioProcess.Id, out visualStudioInstance ) ) { //Find the process you want the VS instance to attach to... DTEProcess processToAttachTo = visualStudioInstance.Debugger.LocalProcesses.Cast<DTEProcess>().FirstOrDefault( process => process.ProcessID == applicationProcess.Id ); //Attach to the process. if( processToAttachTo != null ) { processToAttachTo.Attach(); ShowWindow( (int)visualStudioProcess.MainWindowHandle, 3 ); SetForegroundWindow( visualStudioProcess.MainWindowHandle ); } else { throw new InvalidOperationException( "Visual Studio process cannot find specified application '" + applicationProcess.Id + "'" ); } } } public static Process GetVisualStudioForSolutions( List<string> solutionNames ) { foreach( string solution in solutionNames ) { Process visualStudioForSolution = GetVisualStudioForSolution( solution ); if( visualStudioForSolution != null ) { return visualStudioForSolution; ; } } return null; } public static Process GetVisualStudioForSolution( string solutionName ) { IEnumerable<Process> visualStudios = GetVisualStudioProcesses(); foreach( Process visualStudio in visualStudios ) { _DTE visualStudioInstance; if( TryGetVsInstance( visualStudio.Id, out visualStudioInstance ) ) { try { string actualSolutionName = Path.GetFileName( visualStudioInstance.Solution.FullName ); if( string.Compare( actualSolutionName, solutionName, StringComparison.InvariantCultureIgnoreCase ) == 0 ) { return visualStudio; } } catch( Exception ) { } } } return null; } #endregion #region Private Methods private static IEnumerable<Process> GetVisualStudioProcesses() { Process[] processes = Process.GetProcesses(); return processes.Where( o => o.ProcessName.Contains( "devenv" ) ); } private static bool TryGetVsInstance( int processId, out _DTE instance ) { IntPtr numFetched = IntPtr.Zero; IRunningObjectTable runningObjectTable; IEnumMoniker monikerEnumerator; IMoniker[] monikers = new IMoniker[1]; GetRunningObjectTable( 0, out runningObjectTable ); runningObjectTable.EnumRunning( out monikerEnumerator ); monikerEnumerator.Reset(); while( monikerEnumerator.Next( 1, monikers, numFetched ) == 0 ) { IBindCtx ctx; CreateBindCtx( 0, out ctx ); string runningObjectName; monikers[0].GetDisplayName( ctx, null, out runningObjectName ); object runningObjectVal; runningObjectTable.GetObject( monikers[0], out runningObjectVal ); if( runningObjectVal is _DTE && runningObjectName.StartsWith( "!VisualStudio" ) ) { int currentProcessId = int.Parse( runningObjectName.Split( ':' )[1] ); if( currentProcessId == processId ) { instance = (_DTE)runningObjectVal; return true; } } } instance = null; return false; } #endregion } #endregion }
- Open Mod.cs and add a function
[Conditional("DEBUG")] private void AttachDebugger() { // do a search for any Visual Studio processes that also have our solution currently open var vsProcess = Debugging.VisualStudioAttacher.GetVisualStudioForSolutions( new List<string>() { SolutionInfo.SolutionFileName } ); if( vsProcess != null ) { Debugging.VisualStudioAttacher.AttachVisualStudioToProcess( vsProcess, Process.GetCurrentProcess() ); } else { // try and attach the old fashioned way Debugger.Launch(); } if( Debugger.IsAttached ) { Logger.WriteLine( "[mod] Test472 :: Attched to VisualStudio for debugging" ); } }
- Add to the function "GameContextInitializeInit"
this.AttachDebugger();
- Add a reference to the project to "C:\Program Files (x86)\Common Files\Microsoft Shared\MSEnv\PublicAssemblies\envdte.dll"
You should now be able to simply hit F5 to run your project and have full debugging capabilities.
Interface Documentation
- IAchievementComponentBuilder
- IBiomeBuilder
- ICommandBuilder
- IComponentBuilder
- IEffectBuilder
- IEntityAction
- IEntityLogicBuilder
- IEntityPainterBuilder
- IFarmAnimalEntityBehaviourBuilder
- IItemBuilder
- IItemComponentBuilder
- IModHookV2
- INotificationBuilder
- IPlayerExtendedCommand
- IScript
- ISubProcessHandler
- ITileComponentBuilder
- ITileConfigurationBuilder
- ITileStateBuilder
- ITileStateBuilder
- IVillagerEntityCommand
- IVillagerEntityInstinct
- IWeatherComponentBuilder