Jump to content

[1.1.3] BackgroundProcessing


jamespicone

Recommended Posts

There's a big difference between the resource module data that I store and providing a generic per-module data store for other users - the resource module info isn't really associated with a given partmodule once I've made it.

Ah, fair enough. That wasn't apparent from my cursory examination of your code. I was under the impression you were storing the orientation of specific instances of panels and such, and thus assumed that an association to the actual module was maintained.

How about parts then? Do you have a clear association between the parts and the data that would then allow creation of such data stores on a per part per module *type* basis? I suspect that for my purposes anyways, that would be just as good.

It has to persist across save/load because someone can close the game while a vessel is in the background and then open it again with the same vessel in the background.

Not sure I get that one though. Wouldn't all your stored vessel data be reinitialized if that were to happen?

Incidentally, as of right now FixedBackgroundUpdate isn't called once per PartModule on unloaded vessels. It's called once per PartModule type per part. So if a Part has two PartModules of the same type that both implement FixedBackgroundUpdate, FixedBackgroundUpdate will only get called /once/ on that partmodule type with that partFlightID. That's another one that's a result of trying to sidestep how tricky it is to keep ProtoPartSnapshot module lists in sync with Part module lists.

Not sure if others would consider that a problem, but I can't really think of a circumstance where I would want to have multiple modules of the same type within a single part. Worst case, you could always loop through the individual instances within your FixedBackgroundUpdate function if you *really* needed to do something like that.

To me then, the above is definitely an acceptable compromise, and I definitely realize that compromises are an integral part of making something like this happen :)

Edited by FlowerChild
Link to comment
Share on other sites

Ah, fair enough. That wasn't apparent from my cursory examination of your code. I was under the impression you were storing the orientation of specific instances of panels and such, and thus assumed that an association to the actual module was maintained.

I just copy out the relevant information - for solar panels, charge rate, power curve, position and orientation, the normal that defines the 'live' side, the pivot axis, and whether or not it tracks. I cheat with tracking - I assume it always tracks fast enough to be in the optimal position (and that it can do that without intersecting other bits of the craft) and just pump out as much power as it would in that situation. Compromises, etc.

Not sure I get that one though. Wouldn't all your stored vessel data be reinitialized if that were to happen?

Only if the stored vessel data were written into the ConfigNode. If you were keeping a static collection of some kind with the data around, it wouldn't be restored, and you don't get a call you could use to save it. Unless you hooked a GameEvent I guess, but that's not a very clean solution.

Whereas my addon will get a Save/Load, and I can call a function on your partmodule type to get you to save/load it.

I guess the data could be stored per-partmodule type-per-part, like FixedBackgroundUpdate is currently called. That seems sensible. I'll look into implementation today.

Not sure if others would consider that a problem, but I can't really think of a circumstance where I would want to have multiple modules of the same type within a single part. Worst case, you could always loop through the individual instances within your FixedBackgroundUpdate function if you *really* needed to do something like that.

To me then, the above is definitely an acceptable compromise, and I definitely realize that compromises are an integral part of making something like this happen :)

Edited by jamespicone
Link to comment
Share on other sites

I just copy out the relevant information - for solar panels, charge rate, power curve, position and orientation, the normal that defines the 'live' side, the pivot axis, and whether or not it tracks. I cheat with tracking - I assume it always tracks fast enough to be in the optimal position (and that it can do that without intersecting other bits of the craft) and just pump out as much power as it would in that situation. Compromises, etc.

Yup, I noticed the cheating on the tracking which is certainly fine by me. If it were just for my own purposes I'd probably even go more abstract and not consider orientation to cut down on overhead as BTSM vessels tend to be rather heavy on the number of solar panels :)

Only if the stored vessel data were written into the ConfigNode. If you were keeping a static collection of some kind with the data around, it wouldn't be restored, and you don't get a call you could use to save it. Unless you hooked a GameEvent I guess, but that's not a very clean solution.

Gotcha. Yeah, the data I'm working with is straight out of the config node, so I'm not really concerned with being able to save/load any changes I make during persistent updates (other than the actual resource amounts which you're taking care of in your code). I was considering doing something like that for my fuel processors though, so that I could just buffer the amount of all fuel produced, and then get it to flow into the appropriate tanks via crossfeed once the vessel becomes active again (since the fuel can't be used in the background anyways, there's no point trying to manage flow via crossfeed while it's inactive), so that's a good thing for me to know so I can make other plans there.

I guess the data could be stored per-partmodule type-per-part, like FixedBackgroundUpdate is currently called. That seems sensible. I'll look into implementation today.

That would be quite awesome man. Thanks a bunch :)

Link to comment
Share on other sites

There's a new version of BackgroundProcessing up on KerbalStuff, with data persistence changes.

The big change is that now FixedBackgroundUpdate takes a System.Object by reference:

public static void FixedBackgroundUpdate(vessel v, uint partFlightId, ref System.Object data)

(and similar for the resource-handling version). That object persists across FixedBackgroundUpdate calls.

Also there's two new functions you may implement:

public static void BackgroundLoad(Vessel v, uint partFlightId, ref System.Object data)

Guaranteed to be called once before FixedBackgroundUpdate, gives you a chance to load whatever data you want out of the config nodes (you can also use a null 'data' as a flag in fixedbackgroundupdate for the same purpose)

public static void BackgroundSave(Vessel v, uint partFlightId, System.Object data)

Called during savefile creation, lets you persist background data to config nodes.

Link to comment
Share on other sites

Called during savefile creation, lets you persist background data to config nodes.

Totally awesome man. Thank you :)

One question on the save: are any modifications performed to the config nodes contained within the vessel automatically saved, or do we have to handle that part on our end? Like for example if I have a persistent field within a ProtoPartModuleSnapshot, can I simply modify the field directly and then trust that it will be saved appropriately, or is there more to it than that?

Link to comment
Share on other sites

Totally awesome man. Thank you :)

One question on the save: are any modifications performed to the config nodes contained within the vessel automatically saved, or do we have to handle that part on our end? Like for example if I have a persistent field within a ProtoPartModuleSnapshot, can I simply modify the field directly and then trust that it will be saved appropriately, or is there more to it than that?

I just hook game scene changes (via GameEvents.onLevelWasLoaded) and game state saves (GameEvents.onGameStateSave). I *think* changes to the protovessel config should be persisted in those situations. If it isn't, I'll have to come up with something else.

Link to comment
Share on other sites

public static void BackgroundLoad(Vessel v, uint partFlightId, ref System.Object data)

Actually, looking at this a little more closely, maybe the syntax is eluding me here (I'm primarily a C++ programmer rather than C#), but I can see no way of actually setting the data with the above.

Could you possible provide a small use example here? As I'm reading it, if you set System.Object to another object, you'd only be doing so local to the function, and not actually setting it for storage in background. To my mind, for this to work, you'd either have to return an object from this function and have the calling code then store the return value, or the data parameter would need to be some kind of reference to a reference that could then be set from within the function.

Or is data actually already a reference to a reference given that parameters default to passing by reference in C#, rather than passing by value as happens in C++?

Actually, in writing this, I suspect that's exactly what's happening here, but I'd appreciate confirmation that I'm interpreting the intended usage correctly. It's one of my pet peeves about C# the way the whole pass by reference or pass by value thing isn't required to be explicitly stated and differs between base types and objects :)

Link to comment
Share on other sites

The "ref" keyword means that data is being passed by reference not by value. In this case though, wouldn't it make more sense to use "out"? (it's ref, but it makes it explicit that the value of 'data' going in won't be used inside the function)

Edited by Crzyrndm
Link to comment
Share on other sites

The "ref" keyword means that data is being passed by reference not by value.

Yes, I realize. But in C# any object as a parameter is essentially already a ref (they're all passed by reference, not value, unless you're passing a base type like an int) and thus I suspect by adding another "ref" keyword on top of that, you're essentially creating a ref to a ref, allowing the function to essentially set the reference the calling code is using.

I'm pretty sure that jamespicone has it right here, as that's exactly what is needed in this situation so that you can create and set your own data object. As I said, my only confusion with it is likely the difference between how C++ and C# operate in that regard, as in C++ objects default to passing by value.

In C++ the equivalent code would look something like:

public static void BackgroundLoad(Vessel v, uint partFlightId, System.Object &&data)

With the '&' being the equivalent of the 'ref' keyword in C#. But references to references don't even exist in C++ so you'd likely be using a pointer to a pointer instead, but pointers don't exist in C# so you wouldn't be able to use that here.

Hopefully you can now see why I was a tad confused by this :)

Edited by FlowerChild
Link to comment
Share on other sites

Hopefully you can now see why I was a tad confused by this :)

Ugh, I think I got confused by your confusion :confused:

If I'm reading you/it right, the System.Object is being used as a generic boxing of whatever object you're actually requesting because you know what type you're getting, but overloading is either tedious or impossible (if a non-standard object can be sent through data?)

EDIT

This is just me being curious now. I've never run into the need for a pointer to a pointer before so I'm more wondering as to why it would be used.

Edited by Crzyrndm
Link to comment
Share on other sites

This is just me being curious now. I've never run into the need for a pointer to a pointer before so I'm more wondering as to why it would be used.

For exactly what it's being used for here with the ref to an object in C# ;)

Any confusion here is stemming from layers of abstraction that have been introduced with each new generation of language. A pointer is just a variable containing an address in memory abstracted slightly so that it has an associated type (which was the only way to do it in C). A reference is just an abstracted pointer that makes things a little more convenient and less prone to error (which was introduced in C++). An object passed in C# as a parameter is just an abstracted reference, and that was likely done so people wouldn't pass objects by value all the time when they didn't know any better, but it also tends to confuse geezers like me who are used to having to explicitly specify when something is a reference or pointer.

Thus, in passing a reference to an object in C# you're essentially passing a pointer to a pointer. You're allowing the called function to modify your original reference to that object (not just the contents of the object), and make it refer to something else entirely. With just a pointer to the object, you'd be modifying the contents of the object in the function, but couldn't change which object the calling code was referring to.

In this case we want to be able to create a data object and tell BackgroundProcessing to hang onto it for us for later, so we want to specify an actual object, not just modify the contents of an existing one.

What it all comes down to (and the way I think of it because I used to code in Assembly) is that you're passing an address in memory as a parameter, which points to another address in the calling code, that points to the object, so that the called function can change it to point to another object entirely.

EDIT: Actually, here, I just wrote this up for my mod which is probably a better explanation of what's going on here than anything I've said above:


public static void BackgroundLoad( Vessel vessel, uint uiPartFlightID, ref System.Object customData )
{
bool bIsActive = false;
float fEnergyConsumedRate = 0F;

ProtoPartSnapshot partSnapshot = GetProtoPartFromVessel( vessel, uiPartFlightID );

if ( partSnapshot != null )
{
ProtoPartModuleSnapshot moduleSnapshot = GetProtoModuleSnapshotFromPart( partSnapshot, "BTSMModuleProbePower" );

if ( moduleSnapshot != null )
{
Part partPrefab = PartLoader.getPartInfoByName( partSnapshot.partName ).partPrefab;

if ( partPrefab != null )
{
if ( partPrefab.Modules != null )
{
if ( partPrefab.Modules.Contains( "BTSMModuleProbePower" ) )
{
BTSMModuleProbePower modulePrefab = (BTSMModuleProbePower)partPrefab.Modules["BTSMModuleProbePower"];

if ( modulePrefab != null )
{
bool bCoreDisabled = false;
bool bCoreDamaged = false;

if ( bool.TryParse( moduleSnapshot.moduleValues.GetValue( "coreDisabled" ), out bCoreDisabled ) &&
bool.TryParse( moduleSnapshot.moduleValues.GetValue( "m_bProbeDamaged" ), out bCoreDamaged ) )
{
bIsActive = !(bCoreDisabled || bCoreDamaged );

fEnergyConsumedRate = modulePrefab.energyConsumedRate;
}
}
}
}
}
}
}

customData = new BTSMModuleProbePowerBackgroundUpdateData( bIsActive, fEnergyConsumedRate );
}

The really important bit is that last line:


customData = new BTSMModuleProbePowerBackgroundUpdateData( bIsActive, fEnergyConsumedRate );

Notice how I'm creating a whole new object and setting customData to it, not just manipulating its internal values. That's essentially setting what object is being referred to in the calling code, not just locally within the function.

Edited by FlowerChild
Link to comment
Share on other sites

Intresting, I hope it is flexible enough and doesn't require too much processing

From what I've seen of its internals it's both extremely flexible (especially with the additions in the latest release), and the approach taken within the code is extremely well thought out, so while I do have my performance concerns, particularly where large numbers of solar panels are involved, I think if any method is likely to resolve this issue in a performance friendly way, this is likely it. It's obvious from the code that it was architected with performance in mind.

It's a bit of a shame IMO that this thread has received so little attention the past couple of months. I was planning on checking this out for awhile now but had other things to get off my plate before doing so.

BTW James, on the above, I really think it would help the visibility of the mod greatly if the OP here in this thread were more robust. I can definitely appreciate you not wanting to essentially duplicate code by having the instructions for use in two places and thus having to maintain both, but in this case I think it would really be worth the hassle if you're hoping for this system to gain wider acceptance amongst modders.

I know myself, I was originally linked to the thread here, took one look at the OP and its brevity and thought "ok...this isn't serious". I almost walked away from it entirely as a result but then thought better of it and decided to check the Kerbal Stuff page almost on a whim. I'm very glad that I took the time to do so as it became immediately apparent that my first impressions were not at all accurate when I saw your callback system.

Edited by FlowerChild
Link to comment
Share on other sites

Yeah, the 'ref' keyword here is the equivalent of & in C++ (which I also program in primarily, incidentally!)

'out' might have been more appropriate for the load function, but I'm already using ref for FixedBackgroundUpdate (where it does make sense - you need to take the passed value and possibly mutate the reference), so I figured for consistency I'd use 'ref' for load.

I'll duplicate the KerbalStuff explanation into this post.

If I'm reading you/it right, the System.Object is being used as a generic boxing of whatever object you're actually requesting because you know what type you're getting, but overloading is either tedious or impossible (if a non-standard object can be sent through data?)

The reason it's a System.Object is because the API for background processing is entirely reflection-based - people using it don't need to include a reference to the DLL, and their code will work fine without it being present, it just won't do background processing. Because they can't refer to me, and I can't refer to them, a generic box is all we've got.

Link to comment
Share on other sites

Yeah, the 'ref' keyword here is the equivalent of & in C++ (which I also program in primarily, incidentally!)

Hehe...I suspected you might be a C++ programmer actually, as I don't think I've ever seen C# code that makes such extensive use of what basically amounts to pointers to functions and such. I actually learned a fair amount of C# syntax I wasn't aware of through perusing your code, so I really appreciate all this on the level of being a learning experience as well :)

Link to comment
Share on other sites

Hmmm...something appears to have gone amiss with version 4.0.

None of the following functions seem to be being called within my part module:


public static void BackgroundLoad( Vessel vessel, uint uiPartFlightID, ref System.Object customData )
{
Debug.Log( "BTSMModuleProbePower: background load for " + vessel.vesselName );
}

public static void FixedBackgroundUpdate(Vessel v, uint partFlightID, ref System.Object data)
{
Debug.Log( "BTSMModuleProbePower: simple persistant update for " + v.vesselName );
}

public static void FixedBackgroundUpdate( Vessel vessel, uint uiPartFlightID, Func<Vessel, float, string, float> ResourceRequestFunc, ref System.Object customData )
{
Debug.Log( "BTSMModuleProbePower: Persistant update for " + vessel.vesselName );
}

No debug output is generated, and while I've trimmed down the above to only list the log output at the start, the code that follows to drain power in the background isn't triggering either.

I only included the simple form of FixedBackgroundUpdate() in there as I wanted to test if it was just a problem with the complex form, but neither seem to be called.

Full code for the module below:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace BTSM
{
[KSPModule( "Probe Core" )]
class BTSMModuleProbePower : BTSMPartModule
{
private static int m_iNumOutOfEnergyUpdatesForDamage = 20;

[KSPField( isPersistant = false )]
public float energyConsumedRate = 0.16666668F; // default is same as Stayputnik (6X a stock probe core)

[KSPField( isPersistant = false )]
public bool canBeDisabled = false;

[KSPField( isPersistant = true )]
public bool m_bProbeDamaged = false; // persistant variable to track if the probe has been damaged by lack of power

private float m_fEnergyRequiredLeftovers = 0F; // variable to store any energy remnants required which haven't been consumed on a previous update
private int m_iOutOfEnergyUpdateCount = 0; // acts as buffer to prevent momentary power supply problems due to vanilla bugs causing probe cores to fail

[KSPField( isPersistant = true, guiActive = true, guiActiveEditor = true, guiName = "Probe Core"), UI_Toggle( disabledText="Enabled", enabledText="Disabled" )]
public bool coreDisabled = false;

public bool m_bPreviousCoreDisabled = false;

[KSPAction( "Toggle Probe Core" )]
public void ToggleProbeCoreAction( KSPActionParam param )
{
if ( vessel != null && vessel.IsControllable && canBeDisabled )
{
coreDisabled = !coreDisabled;

RefreshAssociatedWindows();
}
}

public override void OnStart( StartState state )
{
base.OnStart(state);

// FCTEST:
Debug.Log( "BTSMModuleProbePower: Start()" );

if ( !m_bInEditor )
{
if ( m_bProbeDamaged )
{
DamageAssociatedProbeCore();
}
else
{
SetProbeCoreDeactivated( coreDisabled );
}
}
else
{
BaseField coreEnabledField = Fields["coreDisabled"];

if ( coreEnabledField != null )
{
coreEnabledField.guiActive = canBeDisabled;
coreEnabledField.guiActiveEditor = canBeDisabled;
}
}

m_bPreviousCoreDisabled = coreDisabled;
m_fEnergyRequiredLeftovers = 0F;
m_iOutOfEnergyUpdateCount = 0;
}

public override void FixedUpdate()
{
base.FixedUpdate();

// FCTEST:
Debug.Log( "BTSMModuleProbePower: Fixed Update" );

if ( !m_bInEditor )
{
bool bShouldGUIBeActive = canBeDisabled;

if ( m_bProbeDamaged )
{
bShouldGUIBeActive = false;
}
else
{
if ( m_bPreviousCoreDisabled != coreDisabled )
{
SetProbeCoreDeactivated( coreDisabled );

m_fEnergyRequiredLeftovers = 0F;
m_iOutOfEnergyUpdateCount = 0;
}

if ( !coreDisabled )
{
if ( !AttemptToDeductEnergyForUse() )
{
if ( !canBeDisabled )
{
DamageAssociatedProbeCore();

bShouldGUIBeActive = false;
}
else
{
coreDisabled = true;

SetProbeCoreDeactivated( true );

ScreenMessages.PostScreenMessage( part.partInfo.title + " shut itself down due to lack of power.", 10F, ScreenMessageStyle.UPPER_CENTER );

m_fEnergyRequiredLeftovers = 0F;
m_iOutOfEnergyUpdateCount = 0;
}
}
}
else if ( !vessel.IsControllable )
{
bShouldGUIBeActive = false;
}
}

BaseField coreEnabledField = Fields["coreDisabled"];

if ( coreEnabledField != null )
{
if ( coreEnabledField.guiActive != bShouldGUIBeActive )
{
coreEnabledField.guiActive = bShouldGUIBeActive;

RefreshAssociatedWindows();
}
}

UpdateStateString();
}

m_bPreviousCoreDisabled = coreDisabled;
}

private bool AttemptToDeductEnergyForUse()
{
return ConsumeResourceReliable( "ElectricCharge", energyConsumedRate, m_iNumOutOfEnergyUpdatesForDamage, ref m_iOutOfEnergyUpdateCount, ref m_fEnergyRequiredLeftovers );
}

private void DamageAssociatedProbeCore()
{
ModuleCommand associatedCommandModule = FindAssociatedCommandModule();

if ( associatedCommandModule != null )
{
associatedCommandModule.minimumCrew = 1;

if ( !m_bProbeDamaged )
{
m_bProbeDamaged = true;

ScreenMessages.PostScreenMessage( part.partInfo.title + " was damaged due to lack of power.", 10F, ScreenMessageStyle.UPPER_CENTER );
}
}
}

private void SetProbeCoreDeactivated( bool bDeactivated )
{
ModuleCommand associatedCommandModule = FindAssociatedCommandModule();

if ( associatedCommandModule != null )
{
if ( bDeactivated )
{
associatedCommandModule.minimumCrew = 1;
}
else
{
associatedCommandModule.minimumCrew = 0;
}
}
}

private void UpdateStateString()
{
ModuleCommand associatedCommandModule = FindAssociatedCommandModule();

if ( associatedCommandModule != null )
{
string sDesiredStateString = "Operational";

if ( m_bProbeDamaged )
{
sDesiredStateString = "Damaged";
}
else if ( coreDisabled )
{
sDesiredStateString = "Disabled";
}

associatedCommandModule.controlSrcStatusText = sDesiredStateString;
}
}

private ModuleCommand FindAssociatedCommandModule()
{
return (ModuleCommand)FindAssociatedModuleOfType( "ModuleCommand" );
}

public override string GetInfo()
{
string sDescString =
"<b><color=#99ff00ff>Requires:</color></b>\n" +
"- <b>Electric Charge: </b>" + FormatRateString( energyConsumedRate ) + "\n";

if ( canBeDisabled )
{
sDescString += "\n<b>Can Be Safely Powered Down</b>\n";
}
else
{
sDescString += "\n<b>WARNING: Requires constant power to avoid damage</b>\n";
}

return sDescString;
}

/*-----------------[ Background Processing Handling ]----------------------------------------------*/

public static void BackgroundLoad( Vessel vessel, uint uiPartFlightID, ref System.Object customData )
{
// FCTEST
Debug.Log( "BTSMModuleProbePower: background load for " + vessel.vesselName );

bool bIsActive = false;
float fEnergyConsumedRate = 0F;

ProtoPartSnapshot partSnapshot = BTSMUtils.GetProtoPartFromVessel( vessel, uiPartFlightID );

if ( partSnapshot != null )
{
ProtoPartModuleSnapshot moduleSnapshot = BTSMUtils.GetProtoModuleSnapshotFromPart( partSnapshot, "BTSMModuleProbePower" );

if ( moduleSnapshot != null )
{
Part partPrefab = PartLoader.getPartInfoByName( partSnapshot.partName ).partPrefab;

if ( partPrefab != null )
{
if ( partPrefab.Modules != null )
{
if ( partPrefab.Modules.Contains( "BTSMModuleProbePower" ) )
{
BTSMModuleProbePower modulePrefab = (BTSMModuleProbePower)partPrefab.Modules["BTSMModuleProbePower"];

if ( modulePrefab != null )
{
bool bCoreDisabled = false;
bool bCoreDamaged = false;

if ( bool.TryParse( moduleSnapshot.moduleValues.GetValue( "coreDisabled" ), out bCoreDisabled ) &&
bool.TryParse( moduleSnapshot.moduleValues.GetValue( "m_bProbeDamaged" ), out bCoreDamaged ) )
{
bIsActive = !(bCoreDisabled || bCoreDamaged );

fEnergyConsumedRate = modulePrefab.energyConsumedRate;

// FCTEST
Debug.Log( "BTSMModuleProbePower: Backround Processing successfully initialized for: " + vessel.vesselName );
}
}
}
}
}
}
}

customData = new BTSMModuleProbePowerBackgroundUpdateData( bIsActive, fEnergyConsumedRate );
}

public static void FixedBackgroundUpdate(Vessel v, uint partFlightID, ref System.Object data)
{
// FCTEST
Debug.Log( "BTSMModuleProbePower: simple persistant update for " + v.vesselName );
}

public static void FixedBackgroundUpdate( Vessel vessel, uint uiPartFlightID, Func<Vessel, float, string, float> ResourceRequestFunc, ref System.Object customData )
{
// FCTEST
Debug.Log( "BTSMModuleProbePower: Persistant update for " + vessel.vesselName );

// Background Processing callback

if ( customData != null )
{
BTSMModuleProbePowerBackgroundUpdateData probePowerCustomData = (BTSMModuleProbePowerBackgroundUpdateData)customData;

if ( probePowerCustomData.m_bIsActive )
{

float fResourceConsumed = ResourceRequestFunc( vessel, probePowerCustomData.m_fEnergyConsumedRate * TimeWarp.fixedDeltaTime, "ElectricCharge" );

// FCTEST
Debug.Log( "BTSMModuleProbePower: Persistant update for consumed: " + fResourceConsumed );

// FCTODO: Check energy consumed and damage probe core on lack of power
}
}
}
}

class BTSMModuleProbePowerBackgroundUpdateData
{
public bool m_bIsActive;
public float m_fEnergyConsumedRate;

public BTSMModuleProbePowerBackgroundUpdateData( bool bIsActive, float fEnergyConsumedRate )
{
m_bIsActive = bIsActive;
m_fEnergyConsumedRate = fEnergyConsumedRate;
}
}
}

I included debug output in the OnStart() and FixedUpdate() functions as well to verify that the module is indeed being added to the parts, which it is.

Any ideas?

Link to comment
Share on other sites

Are you getting the "Debug.Log("BackgroundProcessing: Running assembly at " + Assembly.GetExecutingAssembly().Location + " (" + Assembly.GetExecutingAssembly().GetName().Version + ")");" debug output? What does it say?

BackgroundProcessing: Running assembly at path\Kerbal Space Program\GameData\BackgroundProcessing-0.4.0.0.dll (0.4.0.0)

Later on getting messages about clearing and saving game state, so BP itself seems to be functioning fine. The callbacks themselves just never seem to be called.

Edited by FlowerChild
Link to comment
Share on other sites

Well it's at least starting up then...

Is the vessel prelaunch? You can tell if it says "Standing by to launch" on the main screen.

The specific test case I am using right now is, yes, as I cleaned out the mods folder of everything other than BTSM and BP to make sure it wasn't a secondary interaction, and created a new save with just a single probe core on the launch pad.

Same thing seems to be happening with existing saves and many vessels in flight however, which is why I trimmed it down to a test case. I was also getting a bunch of debug log output about modules being in the wrong index with Deadly Reentry installed, I assume because it dynamically adds modules to handle reentry.

Launching my simple test case the BackgroundLoad() function is called after I return to the space center, and the simple form of FixedBackroundUpdate() begins to be called each tick.

EDIT:

Further info: Upon exiting the game, and commenting out the simple form to see if the complex would be called, I booted the game back up. Vessel status is now "landed at launch pad" neither BackgroundLoad() or FixedBackgroundUpdate() appear to be called. Making vessel active then returning to space center doesn't start it up either. Making it active, flying it up a bit, then relanding it and returning to space center also fails to net results.

I seemed to get a brief moment there when I first launched it and then returned to space center where BP started calling the callbacks, and that was it.

Surprisingly, recovering that vessel, placing a new one, launching it, landing it back, and returning to space center doesn't seem to start it up again either.

Edited by FlowerChild
Link to comment
Share on other sites

Okay, so prelaunch vessels shouldn't get anything called on them. The reason is because when vessels are put in the launchpad there's a transitory half-initialised vessel created and added to the FlightGlobals.Vessels list that I don't want to do background processing on, and testing for prelaunch caught that problem without too many other problems. My usual testing practice is to attach wheels to the probe core I'm testing and move it a bit - that's usually enough to get it out of prelaunch, as far as I can tell.

And yeah, plugins that add modules at runtime, or changing mods change partmodules that are available, can play merry hell. If there's a difference between the PartModules on a ProtoPartSnapshot and the PartModules on the prefab Part, you should expect background callbacks to happen, but you should expect some weirdness with resource handling. That code can probably be improved, but will probably never be perfect.

Link to comment
Share on other sites

Okay, so prelaunch vessels shouldn't get anything called on them. The reason is because when vessels are put in the launchpad there's a transitory half-initialised vessel created and added to the FlightGlobals.Vessels list that I don't want to do background processing on, and testing for prelaunch caught that problem without too many other problems. My usual testing practice is to attach wheels to the probe core I'm testing and move it a bit - that's usually enough to get it out of prelaunch, as far as I can tell.

Fair enough, but doesn't explain the additional weirdness described in my edit though. Following up on it I just launched another probe to full orbit in my simple test case, and still getting no callbacks.

And yeah, plugins that add modules at runtime, or changing mods change partmodules that are available, can play merry hell. If there's a difference between the PartModules on a ProtoPartSnapshot and the PartModules on the prefab Part, you should expect background callbacks to happen, but you should expect some weirdness with resource handling. That code can probably be improved, but will probably never be perfect.

Hmmm...ok, that may be problematic given my mod essentially requires DR to be installed along side it (not doing that for my test cases here though). Can talk about that separately though as callbacks weren't resulting regardless.

At the moment I'm just trying to reproduce when the callbacks *do* get called, rather than when they don't, as other than that one specific instance I mentioned above right after launch, they aren't happening right now :)

EDIT: Ah...this is very interesting:

Exited game. Restarted. Went back into game and loaded simple test case with probe in orbit. No callbacks.

Exited game again. Uncommented the simple form of FixedBackgroundUpdate(). Restarted. Callbacks happening.

So it looks like the simple form is working, and if it's present, callbacks are working fine. If you just have the complex form I have listed above, no callbacks are triggered.

So either there's a problem with my syntax (you can check it above), or there's something wonky about the complex form in particular. All test cases where I haven't had callbacks have been *only* with the complex form. I'll go back now and check it with DR installed and a more populated save game with the simple form.

EDIT 2: Yup, DR installed, populated save game, simple form of FixedBackgroundUpdate() present, popped into save and got debug messages for a whole bunch of different vessels indicating FixedBackgroundUpdate() was being called. Looks like this is really a problem with the complex form.

Edited by FlowerChild
Link to comment
Share on other sites

That's very strange. I haven't been able to reproduce the problem yet. I assume you're not getting any exceptions in your log - they'd be potentially meaningful. If you're comfortable with compiling BP from source, I can add a bunch of debug statements that might be useful so we can get a feel for where it's falling down.

If Deadly Reentry only adds partmodules at the end of the module list it should be fine - you'll get a bunch of messages like "Ran out of modules before finding module SOME_REENTRY_MODULE", but it shouldn't break anything. You should only worry if it can't find ModuleDeployableSolarPanel, ModuleGenerator, or ModuleCommand.

Link to comment
Share on other sites

That's very strange. I haven't been able to reproduce the problem yet. I assume you're not getting any exceptions in your log - they'd be potentially meaningful. If you're comfortable with compiling BP from source, I can add a bunch of debug statements that might be useful so we can get a feel for where it's falling down.

No, no exceptions at all. I just ran a test where I straight out copy pasted your function prototype listed on Kerbal Stuff for the complex form of FixedBackgroundUpdate() and renamed all my local variables to match your names to make sure I hadn't just made some weird typo somewhere that was preventing it being called, and am still getting nothing on it. So, simple form good, complex form bad apparently :)

And yeah, I'm comfortable compiling from source, and previously set myself up to do so with BP when I was running some initial tests. Before we start inserting debug statements though, can you pass me a code snippet where you're testing with *just* the complex form of the FixedBackgroundUpdate() callback so I can make 100% certain my function definition is kosher? If it's working for you, and not for me, the only thing I can think of is that the definition isn't 100% identical and thus BP isn't recognizing my callback.

Link to comment
Share on other sites

I'm an idiot, and the test case I was looking at wasn't the complex FixedBackgroundUpdate, which I indeed broke (it was expecting a delegate type thing, not a Func<Vessel, float, string float>). Should be fixed in 0.4.0.1, up on kerbalstuff right now.

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