-
Notifications
You must be signed in to change notification settings - Fork 20
MultipleModuleInPartAPI
This API allow other plugins to implement PartModules that can exist in multiple occurrence in a single part and won't suffer "module indexing mismatch" persistent data losses following part configuration changes.
When partmodules are added/removed in a part config following a mod installation/removal/update, the index of the persisted protomodules in existing saves (and of the module ConfigNode in saved ships) won't match the prefab module index anymore.
This mean that next time a changed part will be loaded from its persisted protopart, the protomodule list isn't matching the prefab module list. The stock handling of that situation is arguably quite poor, but the case of multiple same type modules being present in a part fundamentally can't be handled.
The stock code will attempt to find the right module by iterating on all the part modules, and in many cases it will resort to loading the node in the first found module whose type match. This mean that if multiple modules of the same type exists, all protomodule nodes will be loaded in the first occurrence, and other occurrences will be reset to the prefab configuration, essentially completely borking the part configuration.
The ModuleIndexingMismatch KSPCommunityFixes patch override the stock "reconnection" logic to provide better heuristics : it count how many modules of the same type exists in each module list (the saved and instance ones), and load the nth node into the nth module of the same type. However, this doesn't work in case the config change is removing or adding a module that exist/existed in multiple occurrences, but at least it cover all cases of another module type being responsible for the indexing mismatch.
The only way to completely avoid that issue is to define a persisted identifier, unique for each module of the same type in the part config. That identifier can then be checked when loading the persisted data to ensure that data is actually the one corresponding to the module instance. While it is possible to implement that solution through some custom logic in a PartModule.OnLoad()
implementation, this is cumbersome and inefficient.
As a part of its ModuleIndexingMismatch patch, KSPCommunityFixes provides a 0_MultipleModuleInPartAPI.dll
assembly that you can redistribute alongside your plugin, and reference from your own assembly. This assembly contains a IMultipleModuleInPart
interface that you can implement on a PartModule
.
When that interface is implemented, the KSPCommunityFixes ModuleIndexingMismatch patch will be able to reconnect the module persisted data to its instance in all cases.
Due to the KSP 1.12 bug that cause the plugin loader to crash when multiple *.dll
files with the same name exists within the GameData
subfolders, you must rename the redistributed 0_MultipleModuleInPartAPI.dll
to something reasonably unique, like 0_MultipleModuleInPartAPI_MyMod.dll
public interface IMultipleModuleInPart
{
string ModulePartConfigId { get; }
}
Implement this interface on a PartModule derivative if the module can potentially be present multiple times in the same part.
This will ensure that in an existing save or ship, when your module is loaded while the part configuration has been modified due to the user installing/uninstalling/updating mods, the persisted state is loaded into the right module.
To implement that interface :
- Apply the interface to your module.
- Add a
modulePartConfigId
persistedKSPField
to your module. - Implement the
ModulePartConfigId
property so it returns themodulePartConfigId
value.
public class MyModule : PartModule, IMultipleModuleInPart
{
[KSPField(isPersistant = true)]
public string modulePartConfigId;
public string ModulePartConfigId => modulePartConfigId;
}
Then, in the part configs, make sure to assign an unique modulePartConfigId
to each occurrence of the module :
- It doesn't have to be unique over different parts.
- It doesn't have to be unique over different modules implementing the interface.
- It isn't mandatory to define it if there is only one occurrence of the module in a part. However, this is still recommended to future-proof your part config in case you want to add another occurrence of that module latter, or if your module is added by another mod through a MM patch.
PART
{
name = somePart
MODULE
{
name = MyModule
modulePartConfigId = doesSomething
}
MODULE
{
name = MyModule
modulePartConfigId = doesSomethingElse
}
}
PART
{
name = anotherPart
MODULE
{
name = MyModule
// It's okay to use the same id as in "somePart"
modulePartConfigId = doesSomething
}
MODULE
{
name = MyOtherModule
// it's okay to use the same id as in "MyModule", as this isn't the same module type
modulePartConfigId = doesSomething
}
}