Jump to content

Changes to IPartMassModifier, IPartCostModifier, and IPartSizeModifier in build 1209


Recommended Posts

These interfaces (used by many mods to set mass and cost) underwent some major changes in build 1209 (quite possibly what caused so many mods to stop working).  This is the result of this thread in the issue tracker.  The basic idea is that it allows mods which do delta-v or other flight simulations to know how a module's mass might change in flight.  As best I can tell, KSP doesn't do anything with these changes directly.  In this post, I will try to document these changes, but keep in mind that what's presented here is a best guess based on available metadata, the issue mentioned above, and a couple of conversations on IRC.  Will try to update as more information becomes available.

The new interfaced definitions are as follows:

(definitions generated by Visual Studio from DLL metadata)

public interface IPartMassModifier
{
    float GetModuleMass(float defaultMass, ModifierStagingSituation sit);
    ModifierChangeWhen GetModuleMassChangeWhen();
}

public interface IPartCostModifier
{
    float GetModuleCost(float defaultCost, ModifierStagingSituation sit);
    ModifierChangeWhen GetModuleCostChangeWhen();
}

public interface IPartSizeModifier
{
    Vector3 GetModuleSize(Vector3 defaultSize, ModifierStagingSituation sit);
    ModifierChangeWhen GetModuleSizeChangeWhen();
}

The new method GetModuleXXXChangedWhen() informs KSP (or some mod) of how the modifier will change (see enum definitions below with comments added by me)

GetModuleMass/Cost/Size() work as before unless the part's mass is supposed to change on staging (i.e. if GetModuleXXXChangedWhen() returns ModifierChangedWhen.STAGED).  In that case, it should change based on the staging situation, so that simulations know the correct mass before and after staging.  I think that KSP will always pass ModifierStagingSituation.CURRENT when trying to get the correct physics mass in flight (and this is still done every frame regardless).

The two enums are as follows

public enum ModifierStagingSituation
{
    CURRENT = 0,		// Current mass
    UNSTAGED = 1,	// Mass before staging, for sims ... may not be guaranteed to give correct mass after the part has actually been staged
    STAGED = 2		// Mass after staging, for sims
}

public enum ModifierChangeWhen
{
    FIXED = 0,		// Mass will be set once and never changed
    STAGED = 1,		// Mass will change when staged
    CONSTANTLY = 2	// Mass changes constantly
}

Hope that helps a bit...

Edited by blowfish
Link to comment
Share on other sites

3 hours ago, blowfish said:

public interface IPartSizeModifier { Vector3 GetModuleSize(Vector3 defaultSize, ModifierStagingSituation sit); ModifierChangeWhen GetModuleSizeChangeWhen(); }

Any idea how this is supposed to work, or what it is used for? 3 dimensional size does not simply add up like mass or cost.

Edited by pellinor
Link to comment
Share on other sites

6 hours ago, pellinor said:

Any idea how this is supposed to work, or what it is used for? 3 dimensional size does not simply add up like mass or cost.

No idea really.  I think it might add them to determine the ship's size (for runway/launchpad limitations), but I'm not really sure.  I just saw that this was one of the interfaces that changed.

6 hours ago, FreeThinker said:

For IFS I simply return dry mass and current cost

Yup.  These advanced features really only matter if the mass changes on staging, which in this case it doesn't.  Should be safe to ignore ModifierStagingSituation and return ModifierChangeWhen.FIXED for these.

Link to comment
Share on other sites

@FreeThinker I hope you mean you return the delta between prefab mass and dry mass, and prefab cost and dry cost, or you will be implementing the interface incorrectly and leaving the user with more mass and cost than you wish.

 

@pellinor it works just like the others: you report the delta from the prefab.

If the prefab's bounds are 5,6,7 and the current object's bounds are 10,11,5 (and you caused that change) then you should report 5,5,-2.

Link to comment
Share on other sites

Turns out the way IPartSizeModifier was used was...nuts.

I have now fixed it for the next build. However that necessitates a change in what it should return. Since the bounds calculated for an object apart from that modifier are based on the renderer bounds, when the situation passed to GetModuleSize() is CURRENT, you should return 0, unless you want to specify a size different from your actual rendered size. STAGED and UNSTAGED should report as normal. Further, the size passed to the modifier will always be zero.

Note this is a departure from the other modifiers' behavior, which report the prefab value and expect delta from the prefab.

Link to comment
Share on other sites

5 hours ago, NathanKell said:

Turns out the way IPartSizeModifier was used was...nuts

Don't suppose that would be related to how it was being called with atleast two different input values over 4 calls each resize in 1.0.x (? Probably..., can't remember exactly which version I looked at it and then decided it was bonkers) and/or the negative vessel sizes...

Anyway just to make sure I understand this, can you confirm when you say rendered size that is the actual size of the visible mesh for that part instance. Then the (un)staged responses are still the prefab diffs?

Edited by Crzyrndm
Link to comment
Share on other sites

Currently nothing in our code will check size except for the engineer report or for determining launch constraints (same call). That call will pass 0 as the size input. That call will pass CURRENT as the regime.

Oh, crap, also, when saving a Ship Construct we call it to record it in the protopartsnapshot. There, CURRENT is used but the ship size is passed as the size.

 

The bounds reported by the bounding method, prior to PartSizeModifier, are based on the renderer bounds of the given renderer. There used to be multiple calls because there are multiple renderers for some parts. I'd like to say the renderer bounds are correct, but the inflatable heat shield has proved that that is not always the case with skinned meshrenderers. For this reason we support the new field Part.boundsMultiplier to correct for such problems.

Link to comment
Share on other sites

Modified the IPartSizeModifier again based on those points.

New behavior:

During ship bounds calculation it will be called once per renderer bounds now, passed the bounds size. You should report the delta from those bounds (if they are incorrect). In addition, when a ship is saved, it will be called once again, with the last stored base bounds where you should again report offset from those bounds.

 

What remains is that unlike the other modifiers, it is passed data regarding the part's current state, and should report a delta from that state, rather than from prefab.

Link to comment
Share on other sites

  • 9 months later...

I've been trying to implement, that cost of parts with my module changes when the mode of the module is changed in the VAB/SPH. As far as I can figure, the "GetModuleCost" is the thing I need to achieve this. However, every time I put that into my plugin, KSP will not load... So I'm definitely doing it wrong.

What I try to do is something like the following (highlights)

partial class GTI_MultiModeEngineFX : PartModule, IPartCostModifier     //So I've added the IPartCostModifier to the class
{

  private void updatePropulsion()          //This is the update method I call when the mode of the module is triggered
  {
      ...			//Do I need to call GetModuleCost from here?
  }

  public float UpdateCost()
  {
      return 1000f;			//Placeholder for actually calculating the added cost
  }

  public float GetModuleCost(float defaultCost, ModifierStagingSituation sit)		//How I think this should be...
  {
	return UpdateCost();		//Calling a function for dynamic updating of the costs
  }
  public ModifierChangeWhen GetModuleCostChangeWhen()
  {
	return ModifierChangeWhen.FIXED;
  }

}

Does anyone have some advise on how to proceed with modifying costs of part by code?

Edited by Warezcrawler
Link to comment
Share on other sites

KSP not loading points to something more serious going on.  Logs, as usual, probably hold the answer.

You shouldn't need to call GetModuleCost yourself.  KSP will call it automatically when the ship updates in the editor to recalculate the cost.

Also in your code UpdateCost has no return type.  That won't compile...

Link to comment
Share on other sites

2 minutes ago, blowfish said:

KSP not loading points to something more serious going on.  Logs, as usual, probably hold the answer.

You shouldn't need to call GetModuleCost yourself.  KSP will call it automatically when the ship updates in the editor to recalculate the cost.

Also in your code UpdateCost has no return type.  That won't compile...

You are right about the return type thing, that was a copy-paste error on my part. Sorry.

I yes it does point to something more severe, which is why I was asking and giving this highlights example. If the example itself is not missing a key part, and therefore in theory should work, then I at least know that it should work. That info would be great.

5 minutes ago, blowfish said:

KSP will call it automatically when the ship updates in the editor to recalculate the cost.

How will it know when to recalculate? My module does something similar to tank switching, and on that user input it needs to update the costs.

Link to comment
Share on other sites

8 minutes ago, Warezcrawler said:

How will it know when to recalculate? My module does something similar to tank switching, and on that user input it needs to update the costs.

I think it listens for the onEditorShipModified game event.  Most UI events will trigger that automatically.

Edited by blowfish
Link to comment
Share on other sites

Thanks for the answers @blowfish.

18 hours ago, blowfish said:

You shouldn't need to call GetModuleCost yourself.  KSP will call it automatically when the ship updates in the editor to recalculate the cost.

Well after messing around a lot, I got it to work somewhat. I don't know why KSP wasn't loading. But I removed this entirely and implemented from the top once more. Apparently the error I made the first couple of time was avoided this time around :confused:....

However, the calculation is firing all the time, both in editor and in flight.

It tried

public float GetModuleCost(float defaultCost, ModifierStagingSituation sit)
{
  return UpdateCost();
}

public float UpdateCost()
{
  Debug.Log("UpdateCost()");
  return 100000f + selectedPropulsion;
}

public ModifierChangeWhen GetModuleCostChangeWhen()
{
  Debug.Log("GetModuleCostChangeWhen()");
  return ModifierChangeWhen.FIXED;
}

But it seem that GetModuleCostChangeWhen() is not firing at all. And my log is being spammed with "UpdateCost()"....

Basically I do not understand why I would have it fire in flight at all. In the editor I can live with it firing. However, I do not see the point in recalculating unless something is changed. Is there some way of doing this calculation thing manually? Like only calling it manually?

Link to comment
Share on other sites

1 hour ago, blowfish said:

If it is, something is unnecessarily triggering updates (verified this)

Ok. When you say varified, is that that you recreated the behavior?

1 hour ago, blowfish said:

You might be able to figure out by printing System.Environment.StackTrace

Never tried this before. So I guess a Debug.Log() with "System.Environment.StackTrace" in it. I will try that. :cool:

Link to comment
Share on other sites

@blowfish whooaaa..... I got some log from that... Think it might be Kerbal Engineer that is triggering it.....

Spoiler

[LOG 22:20:39.107] UpdateCost() in LoadedSceneIsEditor

[LOG 22:20:39.285] System.Environment.StackTrace
   at System.Environment.get_StackTrace()
   at GTI.GTI_MultiModeEngineFX.GetModuleCost(Single defaultCost, ModifierStagingSituation sit)
   at Part.GetModuleCosts(Single defaultCost, ModifierStagingSituation sit)
   at KerbalEngineer.Extensions.PartExtensions.GetCostDry(.Part part)
   at KerbalEngineer.VesselSimulator.PartSim.New(.Part p, Int32 id, Double atmosphere, KerbalEngineer.LogMsg log)
   at KerbalEngineer.VesselSimulator.Simulation.PrepareSimulation(KerbalEngineer.LogMsg _log, System.Collections.Generic.List`1 parts, Double theGravity, Double theAtmosphere, Double theMach, Boolean dumpTree, Boolean vectoredThrust, Boolean fullThrust)
   at KerbalEngineer.VesselSimulator.SimManager.StartSimulation()
   at KerbalEngineer.VesselSimulator.SimManager.TryStartSimulation()
   at KerbalEngineer.Editor.BuildAdvanced.Update()

[LOG 22:20:39.287] UnityEngine.StackTraceUtility.ExtractStackTrace()
GTI.GTI_MultiModeEngineFX:GetModuleCost(Single, ModifierStagingSituation)
Part:GetModuleCosts(Single, ModifierStagingSituation)
KerbalEngineer.Extensions.PartExtensions:GetCostDry(Part)
KerbalEngineer.VesselSimulator.PartSim:New(Part, Int32, Double, LogMsg)
KerbalEngineer.VesselSimulator.Simulation:PrepareSimulation(LogMsg, List`1, Double, Double, Double, Boolean, Boolean, Boolean)
KerbalEngineer.VesselSimulator.SimManager:StartSimulation()
KerbalEngineer.VesselSimulator.SimManager:TryStartSimulation()
KerbalEngineer.Editor.BuildAdvanced:Update()

Full log file

So where do I go from here. It does at least not seem like my mod is doing it.... 

Link to comment
Share on other sites

You could try to talk to the author of that mod about it.  Hard to say where that would go there since KER has been around for a while and I doubt that behavior has changed any time recently.

But if that fails, having it run every frame isn't the end of the world.  Mass updates run every frame both in the editor and flight even without mods.  A simple calculation running every frame will now impact performance.  You can memoize the value (store the result and just return that) if you want, but that's probably not even worth it.  I think the important part is to make sure that whatever you do there doesn't allocate any memory, since that can very quickly add up if you do it every frame.

Link to comment
Share on other sites

21 hours ago, blowfish said:

You could try to talk to the author of that mod about it.  Hard to say where that would go there since KER has been around for a while and I doubt that behavior has changed any time recently.

But if that fails, having it run every frame isn't the end of the world.  Mass updates run every frame both in the editor and flight even without mods.  A simple calculation running every frame will now impact performance.  You can memoize the value (store the result and just return that) if you want, but that's probably not even worth it.  I think the important part is to make sure that whatever you do there doesn't allocate any memory, since that can very quickly add up if you do it every frame.

Thanks. I will try out the KER thread. Maybe this has been overlooked, or KSP behavior changed since implementation.

Otherwise I will consider storing the value and not calculating unless some boolean have changed... Subsidiary not having cost changes as a feature.

Thanks a lot for advise! I learned something, which makes this great :cool:

" System.Environment.StackTrace" is very helpful indeed.

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

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...