sarbian

[1.4.0-1.7.x] Module Manager 4.0.3 (August 9th 2019) - Right To Ludicrous Speed

Recommended Posts

This is just a heads up. In the breaking ground release Squad has done something I didn't think was valid. I've now seen two places where Squad patches add new nodes for existing parts instead of appending to existing nodes. It apparently doesn't create problems in the stock game, but can cause issues with mod patches.

For example: kerbalEVA is the part definition for the male Kerbal in the default suit. It's originally defined in GameData\Squad\Parts\Prebuilt\kerbalEVA.cfg. There's a different files for each suit and gender combination, but all have the pattern kerbalEVA*. Serenity adds the new pick up rocks science experiment to each Kerbal in  GameData\SquadExpansion\Serentiy\Parts\Prebuilt\kerbalEVA.cfg. The gotcha is that instead of patching the existing kerbalEVA PART node with @PART, the files create a new PART (no '@') with the same name with just the science experiment in it. 

You can see the multiple PART nodes in the ModuleManager.ConfigCache. I guess the partloader merges all the nodes with the same part name together eventually. Mods (like KAS) that add modules to kerbalEVA* need to be careful to catch the correct node definition otherwise it's modules can be duplicated on a part node with unintended consequences. This is one of the things breaking KAS on 1.7.1 launch, it's patches need to be more specific to prevent duplicate KAS modules on each Kerbal.

The second case was less of a problem. The same thing happens with GameData\SquadExpansion\Serenity\Experience\Traits.cfg. A new EXPERIENCE_TRAIT node is created to add skills for Engineers and Scientists related to the deployed ground experiments. This caused patches for mods that expand the list of traits like Extraplanetary Launchpads and MKS to run on multiple nodes duplicating their changes. At least in this case you only get a warning that the trait already exists in the database for each of the duplicates.

Share this post


Link to post
Share on other sites
Posted (edited)
18 hours ago, blowfish said:

Okay, that makes more sense.  Does building the new "welded" part config involve MM patching, or are you putting it in the game database in its final state?

In it's final state - but the idea of being able to apply patches instead can be useful in the long run. However, baby-steps - we don't need to build up Rome in a single shot.

What I need on the short run, in a nutshell:

  1. A standard method to inject new parts into the GameDatabase without stomping on other people's toes.
    1. Handling concurrency, i.e., as more than one can try the stunt at the same time, this mechanism runs the calls in a Critical Region
  2. A standard method, probably similar, to edit an existent part on the same requirements (Critical Region, etc)
  3. A Reflection safe way to allow Add'Ons to check/instantiate the feature, to overcome that nasty Mono's runtime bug.
  4. A strong recommendation to every Add'On author to use that methods, so we can inject/edit/create nodes on the GameDatabase at runtime without wasting scarce resources (as my hair) in the process. :) 

I think we can reach these requirements on this way:

// Names and symbols as example only - no naming suggestions are intended on what follows

using mmgdbu = ModuleManager.GameDatabaseUtils;

void function foobar() {
	List<ConfigNode> nodes_to_add = this.create_a_lot_of_nodes();
	using (ConfigNode node = mmgdbu.Find(<some criteria>) // This locks the thread if the MUTEX cannot be acquired now
	{	// Node and everything inside (including other sub nodes) is now locked on MM.
		node.change("datum", new_value);
		ConfigNode foo = node.get("BAR");   
		foo.add("lisias", "was here!");
		foo.add("moar_nodes", nodes_to_add);
	}
	// The lock is lifted. Any other thread will be able to acquired that node (or its sons) now.
}

ScanSAT, KIS, KAS, TweakScale and everybody else would need to acquired a lock before finishing their business on the GameDatabase - what will ensure integrity as everybody tries to do that at the same time on startup.

This will probably be useful for VAB/SPH editing extensions too on the long run. Some extensions does some computations on the background, and if anyone else changes that same ConfigNode, we can have a crash on the same way TweakScale was getting on Startup in the past.

Edited by Lisias
uh. yeah. mero tyops.

Share this post


Link to post
Share on other sites
Posted (edited)

Hi there. I've been trying to get a small patch working for the past week, scouring the web/threads for information but since I can't figure it out:
Is it normal that you cannot use a | in a HAS block? Or well, you can, but everything after it will be ignored without error.

What I'm trying to do is target a subset of experimentID's and within that subset target the subset that has a xmitDataScalar key <1.
My current (working) patch is this:

Spoiler

@PART[*]:HAS[@MODULE:HAS[#experimentID[*]&#xmitDataScalar[<1]]]:FOR[zJognt]
{
	@MODULE:HAS[#experimentID[temperatureScan]]
	{
		//%xmitCheck = #$xmitDataScalar$ | $experimentID$ | $../name$
		@xmitDataScalar = 1.0
	}
	@MODULE:HAS[#experimentID[barometerScan]]
	{
		//%xmitCheck = #$xmitDataScalar$ | $experimentID$ | $../name$
		@xmitDataScalar = 1.0
	}
	@MODULE:HAS[#experimentID[seismicScan]]
	{
		//%xmitCheck = #$xmitDataScalar$ | $experimentID$ | $../name$
		@xmitDataScalar = 1.0
	}
	@MODULE:HAS[#experimentID[gravityScan]]
	{
		//%xmitCheck = #$xmitDataScalar$ | $experimentID$ | $../name$
		@xmitDataScalar = 1.0
	}
	@MODULE:HAS[#experimentID[atmosphereAnalysis]]
	{
		//%xmitCheck = #$xmitDataScalar$ | $experimentID$ | $../name$
		@xmitDataScalar = 1.0
	}
	@MODULE:HAS[#experimentID[infraredTelescope]]
	{
		//%xmitCheck = #$xmitDataScalar$ | $experimentID$ | $../name$
		@xmitDataScalar = 1.0
	}
}

 

 

But I just can't accept that I can't get it done without having to repeat the 'set key' bit. I've been trying the following, but that only targets those with temperatureScan. :/
I've also tried placing all the experiment types in the first experimentID block but that also did not work. Even dropping the subset of the subset (xmitDataScalar <1) didn't make a difference.

Spoiler

@PART[*]:HAS[@MODULE:HAS[#experimentID[temperatureScan]|#experimentID[barometerScan]|#experimentID[seismicScan]|#experimentID[gravityScan]|#experimentID[atmosphereAnalysis]|#experimentID[infraredTelescope]]]
{
	@MODULE:HAS[#experimentID[temperatureScan]|#experimentID[barometerScan]|#experimentID[seismicScan]|#experimentID[gravityScan]|#experimentID[atmosphereAnalysis]|#experimentID[infraredTelescope]]
	{	
		%xmitCheck = #$xmitDataScalar$ | $experimentID$ | $../name$
		//@xmitDataScalar = 1
	}
}

Note: This is not the exact exact code, tried a lot, deleted a lot, settled for the 'ugly' but working version above (which is exact).

Edited by Jognt

Share this post


Link to post
Share on other sites
Posted (edited)
1 hour ago, Jognt said:

Is it normal that you cannot use a | in a HAS block? Or well, you can, but everything after it will be ignored without error.

Yeah, had the same issue a few month ago, seem to be normal^^

How about addressing every part with module "ModuleScienceExperiment" and if it has the key xmitDataScalar < 1, do your stuff:

@PART[*]:HAS[@MODULE[ModuleScienceExperiment]]
{
	@MODULE[ModuleScienceExperiment]:HAS[#xmitDataScalar[<1]]
	{
		@xmitDataScalar = 1.0
	}
}

Any specific reason why you want to identify the parts by their experiment ID?

Edited by 4x4cheesecake

Share this post


Link to post
Share on other sites
Posted (edited)
10 hours ago, 4x4cheesecake said:

Yeah, had the same issue a few month ago, seem to be normal^^

How about addressing every part with module "ModuleScienceExperiment" and if it has the key xmitDataScalar < 1, do your stuff:


@PART[*]:HAS[@MODULE[ModuleScienceExperiment]]
{
	@MODULE[ModuleScienceExperiment]:HAS[#xmitDataScalar[<1]]
	{
		@xmitDataScalar = 1.0
	}
}

 

Drat.. it works in the PART[] bit, in the NEEDS[] bit, but not in the HAS[] bit? :huh:

I'd rather not address it by going for ModuleScienceExperiment because:
1. I don't want it to apply to stuff like Goo/Science Jr;
2. Some mods utilize their own science module while still utilizing stock experimentIDs (like DMagic and US2);

The goal is a patch that is broad enough to cover every part (stock or modded) that calls for specific stock experimentIDs yet narrow enough to not hit all the science parts.

Edit: The reason for using experimentID is so that I can target all experiments regardless of their module name that are of that type while ignoring other types.
By type I mean 'numerical value'. Any experiment that boils down to 'what does the sensor say?' should be 100% transmittable. But stuff that requires a wise scientist to interpret what he sees should be partial.

Edit2: I guess I could target the rerunnable=true key as it looks like it's only present on the exact experiments I defined, but I'm not sure about the odds of mods adding experiments that would add it for 'non simple' science.

 

Edited by Jognt

Share this post


Link to post
Share on other sites
Posted (edited)
7 minutes ago, Gordon Dry said:

@blowfish in https://github.com/sarbian/ModuleManager/wiki/Module-Manager-Handbook it says:


& or , for "AND"
| for "OR"

Is this also valid for @PART[abc|def] ? Is that an OR or an AND?

Module Manager Syntax page says:

You can use & for AND, | for OR and ! for NOT in the needs listing.

I would imagine that means you can't use it in the PART part, but I suppose it's no guarantee.

Edited by UnanimousCoward

Share this post


Link to post
Share on other sites

I is valid in two contexts:

in :NEEDS block (it means or)

in top-level name checking, i.e. @PART[abc|def], in this case it also means or (since a part can only have one name)

Share this post


Link to post
Share on other sites

Question about HAS:
How do I target 'stuff' that does not have the HAS?

Reason I ask is because of the new deployed experiments. I've always played with Maculator's Science Full Value mod which sets the baseValue to scienceCap, but Breaking Ground's deployed experiments use the baseValue for the amount they create per hour.
So I was looking to target all EXPERIMENT_DEFINITION:HAS[!id[deployed*]] which does not work (tried several versions of ! placement with/without #id instead of id).

I ended up adding a oldBV value to all experiment_definition's, targeting all of them, then setting them back for those with deployed* in their id, and removing the oldBV from all definitions afterwards, but I am wondering if there's a better way.

Share this post


Link to post
Share on other sites
13 minutes ago, Jognt said:

Question about HAS:
How do I target 'stuff' that does not have the HAS?

Reason I ask is because of the new deployed experiments. I've always played with Maculator's Science Full Value mod which sets the baseValue to scienceCap, but Breaking Ground's deployed experiments use the baseValue for the amount they create per hour.
So I was looking to target all EXPERIMENT_DEFINITION:HAS[!id[deployed*]] which does not work (tried several versions of ! placement with/without #id instead of id).

I ended up adding a oldBV value to all experiment_definition's, targeting all of them, then setting them back for those with deployed* in their id, and removing the oldBV from all definitions afterwards, but I am wondering if there's a better way.

in a :HAS block

  • @ means "has node"
  • ! means "does not have node"
  • # means "has value"
  • ~ means "does not have value"

Share this post


Link to post
Share on other sites
Posted (edited)
6 minutes ago, blowfish said:

in a :HAS block

  • @ means "has node"
  • ! means "does not have node"
  • # means "has value"
  • ~ means "does not have value"

Wait, do you mean key or value? If I write "~id[software]" is it going to ignore anything with an "id =" key/value, or is it going to ignore any "id =" that have "software" in it?

Edit: Thank you for the reply :)

Edited by Jognt

Share this post


Link to post
Share on other sites
1 minute ago, Jognt said:

Wait, do you mean key or value? If I write "~id[software]" is it going to ignore anything with an "id =" key/value, or is it going to ignore any "id =" that have "software" in it?

Edit: Thank you for the reply :)

:HAS[#foo[bar]]

  • will match foo = bar
  • will not match foo = baz
  • will not match a node without foo

:HAS[~foo[bar]]

  • will match foo = baz
  • will match a node without foo
  • will not match foo = bar

Share this post


Link to post
Share on other sites
1 minute ago, blowfish said:

:HAS[#foo[bar]]

  • will match foo = bar
  • will not match foo = baz
  • will not match a node without foo

:HAS[~foo[bar]]

  • will match foo = baz
  • will match a node without foo
  • will not match foo = bar

Thank you very much! :)

Share this post


Link to post
Share on other sites

@Lisias okay, so again correct me if I'm wrong, but it sounds like you don't actually need ModuleManager to re-patch the game database (which would take extra time), but to act as a coordinator for other mods recompiling parts via the part loader?

Share this post


Link to post
Share on other sites
13 hours ago, blowfish said:

@Lisias okay, so again correct me if I'm wrong, but it sounds like you don't actually need ModuleManager to re-patch the game database (which would take extra time), but to act as a coordinator for other mods recompiling parts via the part loader?

Yes, it's exactly this. I like the term "Arbitrator" more than "Coordinator", as this feature needs to be somewhat authoritative on the decision making, but it's it. Even by not allowing re-patching (and I never considered this before, this good idea is your fault!! :D ), such arbitration appears to be better implemented on Module Manager due it's inherent nature of handling the GameDatabase.

Third parties being able to recompiling parts via part loader would be one of the features under the authority of such Arbitrator - the problem to be solved is to allow access to GameDatabase under Critical Sections to prevent the Add'Ons triggering a Toe Stomping Fest.

Locking up the whole GameDatabase would be highly inefficient as it would serialize processes that could be executed concurrently, but it's a quick & dirty way to solve the problem for a POC (Proof of Concept).

Share this post


Link to post
Share on other sites

Hey sorry, very newbie mod-writing question:

I'm trying to get my mind around a way to set up something where specific tech tree nodes are reliant on a specific "building level", so for instance maybe you can only unlock heavyRocketry if you have VAB level 2 or something.

I think maybe theres a hacky way to do it by writing a module that reads building levels and then locks a node based on if MM drops a line somehow adding "building level" requirements to specific tech nodes, but i cannot get my mind around how to crack such a thing out.

Is there a smoother system that already exists through MM that i need to read up on? Has anyone seem something similar to this done?

Share this post


Link to post
Share on other sites

Question: can module manager patch the rocsdef.cfg file where Squad keeps the Breaking Ground surface features in order to add them to planet packs (or just mod stock planets) without modifying that file? And if so, how?

Share this post


Link to post
Share on other sites
Posted (edited)
14 hours ago, juanml82 said:

Question: can module manager patch the rocsdef.cfg file where Squad keeps the Breaking Ground surface features in order to add them to planet packs (or just mod stock planets) without modifying that file? And if so, how?

Spoiler

ROC_DEFINITION:NEEDS[GPP,SquadExpansion/Serenity]{
  Type = GaelVolcanicEjecta
  displayName = Volcanic Ejecta
  prefabName = dunaEjecta
  modelName = dunaEjecta
  OrientateUp = true
  Depth = 0.5
  CanBeTaken = true
  Frequency = 5  
  CastShadows = true
  ReceiveShadows = true
  CollisionThreshold = 50
  SmallROC = true
  RandomDepth = true
  RandomOrientation = false
  RandomRotation = true
  localSpaceScanPoints = 0, 0.4, 0
  CELESTIALBODY{
    Name = Gael
    Biome = Pligia Shores
    Biome = Volcano
  }
}

Have yet to find any on the surface, but the syntax is valid, and ConfigCache shows it as proper node.

Edited by Corax
*$+/&% forum software

Share this post


Link to post
Share on other sites
On 6/10/2019 at 11:50 AM, Corax said:
  Reveal hidden contents

ROC_DEFINITION:NEEDS[GPP,SquadExpansion/Serenity]{
  Type = GaelVolcanicEjecta
  displayName = Volcanic Ejecta
  prefabName = dunaEjecta
  modelName = dunaEjecta
  OrientateUp = true
  Depth = 0.5
  CanBeTaken = true
  Frequency = 5  
  CastShadows = true
  ReceiveShadows = true
  CollisionThreshold = 50
  SmallROC = true
  RandomDepth = true
  RandomOrientation = false
  RandomRotation = true
  localSpaceScanPoints = 0, 0.4, 0
  CELESTIALBODY{
    Name = Gael
    Biome = Pligia Shores
    Biome = Volcano
  }
}

Have yet to find any on the surface, but the syntax is valid, and ConfigCache shows it as proper node.

Huh. It doesn't seem to be working, though probably not because of the syntax. Even the ROC finder in the cheat menu finds nothing

Share this post


Link to post
Share on other sites
3 hours ago, juanml82 said:

Huh. It doesn't seem to be working, though probably not because of the syntax. Even the ROC finder in the cheat menu finds nothing

Yes, I'm still figuring it out, like I said I have yet to find any... that snippet was just to answer your question of whether and how it is possible to patch the ROCs.

Share this post


Link to post
Share on other sites

I have a part which has two ModuleAnimateGeneric modules.  The only significant difference between them is the animationName

I need to replace one of them, but I can't assume any specific order.  Is it possible to delete a  module based on a value inside the module?

Share this post


Link to post
Share on other sites
1 hour ago, linuxgurugamer said:

I have a part which has two ModuleAnimateGeneric modules.  The only significant difference between them is the animationName

I need to replace one of them, but I can't assume any specific order.  Is it possible to delete a  module based on a value inside the module?

so something like :

  • @MODULE[ModuleAnimateGeneric]:HAS[name=this]{}
  • @MODULE[ModuleAnimateGeneric]:HAS[name=that]{}

was thinking the same.

Share this post


Link to post
Share on other sites
3 hours ago, zer0Kerbal said:

so something like :

  • @MODULE[ModuleAnimateGeneric]:HAS[name=this]{}
  • @MODULE[ModuleAnimateGeneric]:HAS[name=that]{}

was thinking the same.

That actually worked!  Surprised me, but it was a pleasant surprise

Share this post


Link to post
Share on other sites

Hang on, this doesn’t support 1.7.1? Wow! I think that’s the first time I’ve ever seen module manager be out of date.

Share this post


Link to post
Share on other sites
Posted (edited)
6 hours ago, linuxgurugamer said:

That actually worked!  Surprised me, but it was a pleasant surprise

well blow me up out of an airlock and call me Jeb....hotdiggitydarn and Jiminy Crickets!

can't wait to see the patch now.

Edited by zer0Kerbal

Share this post


Link to post
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.