Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

55 minutes ago, udk_lethal_d0se said:

Have a look at Vessel.activeVessel, there are a number of accessible details there. Also have a look at vessel in the Api, linked below. 

https://anatid.github.io/XML-Documentation-for-the-KSP-API/class_vessel.html

Yes, but how do I actually use that information? Do I use

double d = Vessel.activeVessel.verticalSpeed;

or what?

Link to comment
Share on other sites

@0111narwhalz The currently focused vessel is referenced as a static at FlightGlobals.ActiveVessel.

If you want to refer to a non-active vessel, getting the reference is trickier and depends on where the vessel you are trying to access is.

Notably if you are in a partModule, using this.vessel instead recommended as that is the vessel that partModule is attached to.

D.

Edited by Diazo
Link to comment
Share on other sites

5 hours ago, Booots said:

I think what he means is that the root cause is the part's orientation being incorrect because it's constantly changing. His suggestion would be to update and correct each part's position and orientation at each tick so that when it comes time to save or pack the part its orientation and position don't need adjusting because they're already up to date. Obviously this comes with the computation overhead of calculating and correcting the orientation of every part in the scene at every tick as opposed to the times when it absolutely needs to be correct.

Well, it will solve the problem but doing this stuff in every tick is barely a good idea performance wise. Doing it less frequent (like once or twice per a second) could be a compromise but the approach is still ugly.

6 hours ago, Booots said:

Does onGameStateSave fire before anything is persisted? If so, you could add something there to go through and fix orientations.

All on save handlers which I was able to find (including this one) are called after the part was actually stored into config node.

Link to comment
Share on other sites

@IgorZ Is this pivotJoint your code or Squads? If it's your code it would be easy enough to update orgPos and orgRot whenever whatever the changePivotAngle event is fires.

If it's Squads code I'm not sure. If you can't find a changePivotAngle event your only choice is to monitor it. Having said that, just running numbers is very little overhead. For AGX I monitor every default action and it's assigned action groups 3 or 4 times a second and that isn't noticeable for overhead.

You can't ignore the overhead it generates, but if it doesn't affect the Physics system or the rendered graphics it's not something you have to worry about 99% of the time.

D.

edit: Actually, I just checked my code and I only check the currently selected action group for the current part, not all groups on the entire vessel, so that is negligable overhead.

Edited by Diazo
Link to comment
Share on other sites

20 minutes ago, Diazo said:

The currently focused vessel is referenced as a static at FlightGlobals.ActiveVessel.

If you want to refer to a non-active vessel, getting the reference is trickier and depends on where the vessel you are trying to access is.

Notably if you are in a partModule, using this.vessel instead recommended as that is the vessel that partModule is attached to.

D.

Okay, so how does one determine that one is in a partModule or not? And how do you tell a part to have your module?

Also, is the snippet I posted earlier incorrect in favor of

double d = FlightGlobals.ActiveVessel.verticalSpeed;

 

Link to comment
Share on other sites

You are using a part module if you're adding it through part configs or the class can trace it's inheritance heirachy through the PartModule class. In that case

double vSpeed = vessel.verticalSpeed; // all part modules have a reference to the part and vessel they are part of

If you're inheriting from Monobehaviour and using the KSPAddon attribute (a partless plugin), your snippet above will get the vSpeed of the vessel the player is currently controlling

Link to comment
Share on other sites

21 minutes ago, Diazo said:

@IgorZ Is this pivotJoint your code or Squads? If it's your code it would be easy enough to update orgPos and orgRot whenever whatever the changePivotAngle event is fires.

Pivot joint feature is available from Squad but there is no "change angle event" as such. The angle is changed by game's physics (e.g. when a force is applied to the vessel).

Link to comment
Share on other sites

51 minutes ago, 0111narwhalz said:

Okay, so how does one determine that one is in a partModule or not? And how do you tell a part to have your module?

Also, is the snippet I posted earlier incorrect in favor of


double d = FlightGlobals.ActiveVessel.verticalSpeed;

 

You'll know your in a partModule because you inherit the class you are writing from PartModule and you add to a part with ModuleManager. You are actually asking a more basic question then I though you were, have you read through the stickies in the Plugin forum and poked around other peoples mods? That's how all of us got started way back when.

40 minutes ago, IgorZ said:

Pivot joint feature is available from Squad but there is no "change angle event" as such. The angle is changed by game's physics (e.g. when a force is applied to the vessel).

Ouch. In that case, does the position being just slightly out cause issues? My first though at a solution is to stick a FOR loop in a VesselModule that checked the orgPos/orgRot values on each part of the vessel and updated them if needed. To keep overhead down, remember the FOR loop integer index and just check 10 parts per physics update frame. That would be acceptable overhead I think and still have near real-time values on all but the absolute largest vessels. If the value has to be exact then you'd have to check every part every frame and hope the overhead is acceptable.

Pseudo-code free-handed:

public class VesselModuleUpdatePartPositions : VesselModule
{
	int index = 0;
	public void FixedUpdate()
	{
	for (int i = 1;i++;i <= 10)
    {
    	Part p = this.vessel.parts[index];
        if(boolean check if orgPos/orgRot have changed on part p)
        {
        	//update values here
        }
        index = index +1;
        if(index > this.vessel.parts.count)
      	{
      		index = 0;
      	}
    }
    }                         
}

Or something. This works because updated the part orgPos/orgRot doesn't have to be part of the rest of your mod, it just makes them accurate for your mod's use.

Edited by Diazo
Link to comment
Share on other sites

2 hours ago, Diazo said:

Ouch. In that case, does the position being just slightly out cause issues?

Positions that are slightly out make no issue to the joint but what does make issues is inconsistent positions on the parts within the vessel. In such case some parts may get shifted relative to the other vessel's parts. I stumbled upon it when tried to update in OnSave: the part of the vessel that was saved before the update had old values in the save file.

2 hours ago, Diazo said:

My first though at a solution is to stick a FOR loop in a VesselModule that checked the orgPos/orgRot values on each part of the vessel and updated them if needed.

Thanks for idea of using VesselModule. One way or another I'll have to deal with the position/rotation somewhere, and vessel module looks the right place for this code.

I'm also going to try detecting vessel's situation change. Saving capability is blocked in some situations (e.g. "moving on surface"), and I could use the trigger to update the vessel since pivot is usually changed by some user or physics interaction (i.e. as a result of situation change).

Link to comment
Share on other sites

@sarbian @Booots @Diazo

Guys, thanks for your suggestions. Eventually, I figured out a workaround. It's very far from being cute but at least it doesn't eat performance.

I do update of the position/rotation in two steps:

  1. In PartModule.OnSave I update all children parts. It doesn't make sense to waste CPU on updating parent part(s) since they either were already updated or don't need it at all (in case of a pivot part was first in the sequence). The problem at this moment is that the part itself is already saved with the wrong values. That's why we need step #2.
  2. In GameEvents.onProtoPartSnapshotSave I check if "my" part is being saved, and if it is then the incorrect values are fixed in the config node itself:
      void onProtoPartSnapshotSave(GameEvents.FromToAction<ProtoPartSnapshot, ConfigNode> action) {
        if (isLinked && action.to != null && action.from.partRef == part) {
          var node = action.to;
          node.SetValue("position", part.orgPos);
          node.SetValue("rotation", part.orgRot);
        }
      }

Note. There are some tricks with regard to parent-to-child relation in this approach not reflected in the code snippet. Vessel's root part can change due to docking or a KIS/KAS action, and the hierarchy can get rebuilt so what that parent-child relation is reversed. In my case such situations can be detected pretty easily.

I don't like step #2 since it's a source of problems in case of major game change, but it's best I can do for now. Stealing FPS from player just to have parts updated for a probable saving was not an option for me. I've created a feature request for this matter, if it gets fixed one day then the workaround above won't be needed.

Link to comment
Share on other sites

ModuleDockingNode myDockingModule = part.Modules.GetModule<ModuleDockingNode>();
ModuleDockingNode dockedTo = myDockingModule.otherNode;
Part partDockedTo = dockedTo.part;

I assume "otherNode" will be null when not docked to anything

Link to comment
Share on other sites

@katateochi Get the docking module on your part, then check if that module's "otherNode" field is null.


			ModuleDockingNode node = part.FindModuleImplementing<ModuleDockingNode>();

			if (node.otherNode != null)
			{
				//Do something
			}

Also, otherNode is not null both when docked to another vessel, and when a docking node is tracking another node (ie the magnets are on).

 

Edited by DMagic
Link to comment
Share on other sites

Hi all, 

As a personal quality of life improvement I wrote a tiny modulemanager cfg that adds a decouple function to the stock heatshields ( basically what I thought 'decouple shroud' was supposed to do..

@PART[HeatShield*]
{
    MODULE
    {
        name = ModuleDecouple
        ejectionForce = 100
        explosiveNodeID = bottom
    }
}

For some reason this stopped working in 1.2 and frankly, I have no idea why.

Can someone explain to me why this doesn't work anymore? Perhaps suggest how to fix the problem?

Link to comment
Share on other sites

2 hours ago, Faile said:

Can someone explain to me why this doesn't work anymore? Perhaps suggest how to fix the problem?

Not sure why it doesn't work, so can't answer that. I however did make this cfg based on yours and the stock squad decoupler / heatshield configs as examples. Seems to work fine with the minimal testing I did before posting it :)

 

@PART[HeatShield*]
{
	MODULE
	{
		name = ModuleDecouple
		ejectionForce = 100
		isOmniDecoupler = false
		menuName=Decouple Heat Shield
		stagingEnabled = true
		stagingEnableText = HS Decouple Not Staged
		stagingDisableText = HS Decouple Staged
	}
}

 

Edited by K0D3R
syntax
Link to comment
Share on other sites

I got it to work, thanks for the idea of putting all the configs in one section

It needed one more change, isOmniDecoupler = false is the default, so basically your cfg is the same as stock, also, it doesn't work as my mod does.

The user story is this:

"While building my ship, I want to be able to decouple anything attached to the heatshield while keeping the heatshield in place without adding a decoupler."

Your config only lets you decouple the heatshield itself ( same as stock functionality ).

Here's the working cfg:

@PART[HeatShield*]
{
    MODULE
    {
        name = ModuleDecouple
        ejectionForce = 100
        menuName=Decouple From Below Heat Shield
        stagingEnabled = true
        stagingEnableText = HS Decouple Not Staged
        stagingDisableText = HS Decouple Staged
        explosiveNodeID = bottom
    }
}

I'll be posting this to spacedock and CKAN in a bit.

Link to comment
Share on other sites

Is there any way in a CFG I can include a RetractableLadder module in the default "Gear" action group?  (So my ladders extend when my landing gear do, without having to set that up for each craft - similar to the way lights have a defaultActionGroup property)

Edited by Fwiffo
Link to comment
Share on other sites

1 hour ago, Fwiffo said:

Is there any way in a CFG I can include a RetractableLadder module in the default "Gear" action group?  (So my ladders extend when my landing gear do, without having to set that up for each craft - similar to the way lights have a defaultActionGroup property)

I think there might be a way, but you'd have to try it.  Try binding the action you want in the editor, and saving the craft.  Then look at the craft file, and find the module you are interested in.  There should be an ACTIONS node, with a subnode for the one you want.  Try putting that into the part's original cfg with the same structure (PART, MODULE, ACTIONS, <action name>).  I haven't tried this but I think there's a good chance it will work

Link to comment
Share on other sites

7 hours ago, blowfish said:

I think there might be a way, but you'd have to try it.  Try binding the action you want in the editor, and saving the craft.  Then look at the craft file, and find the module you are interested in.  There should be an ACTIONS node, with a subnode for the one you want.  Try putting that into the part's original cfg with the same structure (PART, MODULE, ACTIONS, <action name>).  I haven't tried this but I think there's a good chance it will work

Thanks!  Will try that out.

Link to comment
Share on other sites

Any pointers on trying to track down a memory exception error for a Part Module. Debugging in Visual Studio only goes so far as this ends up being a KSP exception

Here is some analysis of the crash dump in case someone sees something I am missing

Spoiler

>	KSP_x64_Dbg.exe!RenderNodeQueue::Cleanup(void)	Unknown


PROBLEM_CLASSES: 

*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

DUMP_CLASS: 2

DUMP_QUALIFIER: 400

CONTEXT:  (.ecxr)
rax=17c1d117f2003df9 rbx=0000000000000000 rcx=0000000000b4ee40
rdx=0000000000000001 rsi=000000001d280570 rdi=00000000204ad870
rip=00007ff77c93e787 rsp=0000000000b4ed90 rbp=0000000000000198
 r8=000000000000000d  r9=0000000000b4ee40 r10=ffffffffffffffff
r11=000000000001b2c8 r12=00000000b4679060 r13=0000000000b4ee40
r14=0000000000000001 r15=0000000000000000
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
KSP_x64_Dbg!CallbackArrayBase<void (__cdecl*)(IndexList const * __ptr64,RendererCullData const * __ptr64),void (__cdecl*)(void const * __ptr64,IndexList const * __ptr64,RendererCullData const * __ptr64)>::Register+0x71b7:
00007ff7`7c93e787 ffd0            call    rax {17c1d117`f2003df9}
Resetting default scope

FAULTING_IP: 
KSP_x64_Dbg!CallbackArrayBase<void (__cdecl*)(IndexList const * __ptr64,RendererCullData const * __ptr64),void (__cdecl*)(void const * __ptr64,IndexList const * __ptr64,RendererCullData const * __ptr64)>::Register+71b7
00007ff7`7c93e787 ffd0            call    rax

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 00007ff77c93e787 (KSP_x64_Dbg!CallbackArrayBase<void (__cdecl*)(IndexList const * __ptr64,RendererCullData const * __ptr64),void (__cdecl*)(void const * __ptr64,IndexList const * __ptr64,RendererCullData const * __ptr64)>::Register+0x00000000000071b7)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 0000000000000000
   Parameter[1]: ffffffffffffffff
Attempt to read from address ffffffffffffffff

PROCESS_NAME:  KSP_x64_Dbg.exe

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  0000000000000000

EXCEPTION_PARAMETER2:  ffffffffffffffff

FOLLOWUP_IP: 
KSP_x64_Dbg!CallbackArrayBase<void (__cdecl*)(IndexList const * __ptr64,RendererCullData const * __ptr64),void (__cdecl*)(void const * __ptr64,IndexList const * __ptr64,RendererCullData const * __ptr64)>::Register+71b7
00007ff7`7c93e787 ffd0            call    rax


INVALID_POINTER_READ
    Tid    [0x5e8]
    Frame  [0x00]: KSP_x64_Dbg!CallbackArrayBase<void 

IN_CALL
    Tid    [0x5e8]
    Frame  [0x00]: KSP_x64_Dbg!CallbackArrayBase<void 
    Failure Bucketing


BUGCHECK_STR:  INVALID_POINTER_READ_IN_CALL

DEFAULT_BUCKET_ID:  INVALID_POINTER_READ_IN_CALL

LAST_CONTROL_TRANSFER:  from 00007ff77c93e88e to 00007ff77c93e787

STACK_TEXT:  
00000000`00b4ed90 00007ff7`7c93e88e : 00000000`00b4ee40 00000000`00000000 00000000`1d280570 00000000`204ad870 : KSP_x64_Dbg!CallbackArrayBase<void (__cdecl*)(IndexList const * __ptr64,RendererCullData const * __ptr64),void (__cdecl*)(void const * __ptr64,IndexList const * __ptr64,RendererCullData const * __ptr64)>::Register+0x71b7
00000000`00b4edd0 00007ff7`7c974537 : 00000000`00000001 00000000`00000000 00000000`1d280570 00000000`204ad870 : KSP_x64_Dbg!CallbackArrayBase<void (__cdecl*)(IndexList const * __ptr64,RendererCullData const * __ptr64),void (__cdecl*)(void const * __ptr64,IndexList const * __ptr64,RendererCullData const * __ptr64)>::Register+0x72be
00000000`00b4ee00 00007ff7`7c97529b : 00000000`00000000 00000000`00000000 00000000`00b5ef31 00000000`1d280570 : KSP_x64_Dbg!IntermediateRenderer::GetCachedWorldAABB+0x5a7
00000000`00b5eec0 00007ff7`7c8fea6c : 00000000`00000000 00000000`1dfa4b00 00000000`00000000 00000000`04b08b60 : KSP_x64_Dbg!IntermediateRenderer::GetCachedWorldAABB+0x130b
00000000`00b5ef90 00007ff7`7c901997 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00b6f2a0 : KSP_x64_Dbg!Camera::DoRender+0x10c
00000000`00b5f020 00007ff7`7c902951 : 00000000`9b894fd0 00000000`044de8e0 00000000`00000000 00000000`bfe65db0 : KSP_x64_Dbg!Camera::Render+0x1d7
00000000`00b6f220 00007ff7`7c94b493 : 00000000`9b894fd0 00000000`204ad870 00007ff7`7da785c8 00000000`044d69f0 : KSP_x64_Dbg!Camera::Render+0x11
00000000`00b6f260 00007ff7`7cbebf17 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KSP_x64_Dbg!IAnimation::IAnimation+0x1f23
00000000`00b6fa80 00007ff7`7cbec271 : 00000000`00000000 00000000`00000000 00000000`044f9560 00000000`00000000 : KSP_x64_Dbg!CallbackArray1<bool>::Invoke+0x6e7
00000000`00b6fac0 00007ff7`7cbeca5a : 00000000`00000056 00000000`000180aa 00000000`000082fc 00000000`00000000 : KSP_x64_Dbg!CallbackArray1<bool>::Invoke+0xa41
00000000`00b6faf0 00007ff7`7cd1a0e0 : 00000000`000007ec 00000000`00000000 00000000`00000001 00000000`00000001 : KSP_x64_Dbg!CallbackArray1<bool>::Invoke+0x122a
00000000`00b6fb40 00007ff7`7cd1a8da : 00000000`000007ec 00000000`00b6fce0 00000000`00000000 00000000`00000000 : KSP_x64_Dbg!IntermediateRenderer::GetLayer+0x3450
00000000`00b6fb70 00007ff7`7cd1e5bc : 00000000`00000000 00000000`00000000 00000000`00000004 00000000`00b6fce0 : KSP_x64_Dbg!PlayerMainWndProc+0x6ba
00000000`00b6fbe0 00007ff7`7d27bbe0 : 00000000`00000000 00000000`00000000 00007ff7`7c8a0000 00000000`00000000 : KSP_x64_Dbg!PlayerWinMain+0xf1c
00000000`00b6fe00 00007ffe`e64f8364 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KSP_x64_Dbg!RectT<int>::GetRight+0xa5650
00000000`00b6feb0 00007ffe`e6ec5e91 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0x14
00000000`00b6fee0 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21

 

 

Trying to troubleshoot an issue with BDArmory Rotary Bomb Rail. I can edit the part (right click has gui options) just fine the first time. Once you move your mouse off the part then select the part again the game crashes.  Part module code can be found here

Edited by gomker
Link to comment
Share on other sites

When you use "Control from Here", is the "from here" always the origin (typically CoM) of the selected part?

I ask because I know when you include docking ports in a weldment, and say "Set as target", the game seems smart enough to use the docking port coordinates as the target rather than the selected part's origin/CoM.  (Which is great; although I'd love to know how that works, e.g. if it looks for something specific in the mesh...)

EDIT: Nevermind I found this which I think will answer my question.

Edited by Fwiffo
Link to comment
Share on other sites

Let's say I create a subclass 'SheepVesselModule' based on the KSP's existing class 'VesselModule'. Then, I just compile and launch KSP without any further action. I observe that the KSP actually accepts and integrates my "orphan" class into every existing vessel. It even calls some methods like Start() too.

Is this one of the Unity's strengths, or I misinterpret the result?

Edited by TaxiService
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...