Optional Dependencies

How does one go about implementing support for optional dependencies? With c++ code, I assume I can't import header files from said mod, as that wouldn't work if it doesn't exist. If this is the case, how can I go about editing some value on some object from that mod, if I can't import any type data for the mod? Are preprocessor directive the way to do this? If so what do I to test if a mod exists with them?
41 Replies
Rex
Rex•2mo ago
1. UE reflection 2. conditionally-loaded C++ modules
BlueBeka
BlueBekaOP•2mo ago
Any links to docs I can look into?
Rex
Rex•2mo ago
Not that I can think of at the moment They're not Satisfactory-specific concepts so try looking in general UE stuff. Maybe look at plugins, they might do stuff like that
BlueBeka
BlueBekaOP•2mo ago
all good. I do some googling... ...tomorrow
AngryBeaver
AngryBeaver•2mo ago
Importing Another Mod :: Satisfactory Modding Documentation
The modding documentation requires time to update and can only cover so much. Looking at other mods' source code is a great way to expand yo...
AngryBeaver
AngryBeaver•2mo ago
that's the most docs we have on it
Rex
Rex•2mo ago
This is not so much about importing another mod, but rather making it an optional dependency For C++ stuff you can make another module that is only loaded if the dependencies are met
AngryBeaver
AngryBeaver•2mo ago
But you can also do it in blueprints with a life cycle event. I remember more details on it https://docs.ficsit.app/satisfactory-modding/latest/Development/ModLoader/ModModules.html touches on it but I think @Robb (Busy) might know more?
Rex
Rex•2mo ago
Ah yes, you can also do stuff like that But what I'm not sure is how to deal with stuff on the BP side
AngryBeaver
AngryBeaver•2mo ago
AFAIK the blueprint links wont be engaged if the content isn't loaded and we use an asset registry that the items have to reference themselves with. which is why a conditional module load is fine so long as you properly firewalled the content. Nothing satops you form CDO'ing your own content to integrate it better with optional content.
Robb
Robb•2mo ago
It depends on what you are trying to do differently when that optional dependency is present, and if it's implemented in BP or cpp If you need a new cpp data structure that the other mod adds, I'm not sure how If it's blueprint then the mod loading library, separate mod modules, soft class paths, and cdo edits are your friends
Rex
Rex•2mo ago
Separate mod modules works for C++ stuff
Robb
Robb•2mo ago
as in C++ implemented mod module, or cpp module Want to make sure I'm following the terms
Rex
Rex•2mo ago
Oh, sorry, got confused there I mean C++ modules, not SML's mod modules
BlueBeka
BlueBekaOP•2mo ago
conditionally-loaded C++ module
I tried this approach but it seems if I statically reference another mod, that mod has to be present even if I never load the module that makes said reference. (Or I'm somehow loading the module without knowing) I tried wrapping the the #include in THIRD_PARTY_INCLUDES_START and THIRD_PARTY_INCLUDES_END but the error persisted
Rex
Rex•2mo ago
No, that's not a conditionally-loaded C++ module In the .uplugin you should have two modules, the first one is your default C++ module and the other one should be the one with dependencies
"Modules": [
{
"Name": "Th3SeaWorld",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "Th3SeaEarly",
"Type": "Runtime",
"LoadingPhase": "PostConfigInit"
}
],
"Modules": [
{
"Name": "Th3SeaWorld",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "Th3SeaEarly",
"Type": "Runtime",
"LoadingPhase": "PostConfigInit"
}
],
(this is just an example of having 2 C++ modules, I just woke up)
BlueBeka
BlueBekaOP•2mo ago
Is there any examples I can look at?
Rex
Rex•2mo ago
Not that come to mind Other than looking at engine plugins
BlueBeka
BlueBekaOP•2mo ago
Mod Modules :: Satisfactory Modding Documentation
Mod Modules are a system provided by SML that allows for simple hooking into the engine life-cycle at key locations important for Satisfacto...
Rex
Rex•2mo ago
That's not what I'm referring to
Robb
Robb•2mo ago
As a sort of example, the alpakit thing that adds a Cpp module for you only adds one. The Cpp docs walk you through adding another manually
Rex
Rex•2mo ago
That would be the beginning, yes Then you have to look into how to specify module dependencies and the like
BlueBeka
BlueBekaOP•2mo ago
I kinda have things working. I still need to play around with things a lot more though. Will update later once I've got a bit more working, tomorrow maybe.
SirDigby
SirDigby•2mo ago
Kind of late to all this, but this is how I do it in Digby Tool
No description
Rex
Rex•2mo ago
Oh right, I forgot you did that thing
Robb
Robb•2mo ago
Link eater
Rex
Rex•2mo ago
We have a thing about this in the docs I believe
Robb
Robb•2mo ago
I think that's the approach Jarno used to add ApCpp but it's definitely not optional >docsearch third party
FICSIT-Fred
FICSIT-Fred•2mo ago
Third Party Libraries :: Satisfactory Modding Documentation
Adding third-party Cā€+ā€+ libaries to your mod is possible but requires some additional setup. The third party library must be included as a ...
Robb
Robb•2mo ago
Don't have the time to go through and compare the two but I think the Georgy link goes into more detail
Rex
Rex•2mo ago
What I meant is that both what we have over there in the docs and the Georgy link are about the same thing: adding a 3rd-party library
BlueBeka
BlueBekaOP•2mo ago
So I've managed to get things working šŸŽ‰ Here's a break down of what I did: To summarize the problem: I have some c++ code that imports a header file from FicsitFarming and I want to run this code only if that mod is present. The rest of the mod should be able to run just fine regardless of if that mod is present or not. As I'm importing this header file, I can't simply do what @SirDigby {Mod Name} did as the header file import will require the mod to be present regardless of if any code using it is called. To get around this I put all the code that requires the mod to be present in a separate module as suggested by @Rex [they/them]. This module is then only loaded when required.
"Modules": [
{
"Name": "BlueBekaTweaks",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "BlueBekaTweaksFicsitFarming",
"Type": "Runtime",
"LoadingPhase": "None" // <- Don't load automatically
}
],
"Plugins": [
{
"Name": "SML",
"Enabled": true,
"SemVersion": "^3.11.1"
},
{
"Name": "FicsitFarming",
"Enabled": true,
"SemVersion": "^4.2.34",
"Optional": true // <- Mark Optional
}
]
"Modules": [
{
"Name": "BlueBekaTweaks",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "BlueBekaTweaksFicsitFarming",
"Type": "Runtime",
"LoadingPhase": "None" // <- Don't load automatically
}
],
"Plugins": [
{
"Name": "SML",
"Enabled": true,
"SemVersion": "^3.11.1"
},
{
"Name": "FicsitFarming",
"Enabled": true,
"SemVersion": "^4.2.34",
"Optional": true // <- Mark Optional
}
]
Note: FicsitFarming is added to thePrivateDependencyModuleNames array in BlueBekaTweaksFicsitFarming.Build.cs so that the header can be found. To load the module on demand, some c++ code needs to run. This could either be done in my main module file or in one of the root modules, either the GameInstanceModule or the GameWorldModule (I'll go with GameWorldModule for now) - I couldn't figure out how to make a c++ module a root module, so instead I created a c++ parent class for my root modules. The functions ModuleManager.IsModuleLoaded(DependancyModName) and ModuleManager.LoadModule(MyDependantModule) are then used to conditionally load my extra module. Once the module is loaded, I can then spawn child GameWorldModules by first using LoadClass followed by SpawnChildModule.
BlueBeka
BlueBekaOP•2mo ago
Feel free to provide any feedback and ways to improve things, I'd much appreciate it. @Rex [they/them] I believe you said something about hard coded string being a bad way to do things - what would be the better way in my case?
Rex
Rex•2mo ago
I couldn't figure out how to make a c++ module a root module, so instead I created a c++ parent class for my root modules.
This is one of the few times where doing something in an UObject's constructor is fine: bRootModule = true;
I believe you said something about hard coded string being a bad way to do things - what would be the better way in my case?
You already "hardcode" the FicsitFarming string in the .Build.cs file, so I don't think that's a problem :wonke: Also, remember that FName is case-insensitive, but AFAIK mod references are case-sensitive. I would use a regular FString instead. Oh, by hardcoding strings, do you mean this?
UClass* ModuleClass = LoadClass<UModModule>(nullptr, TEXT("/Script/BlueBekaTweaksFicsitFarming.BBT_FicsitFarming_CDOs"));
UClass* ModuleClass = LoadClass<UModModule>(nullptr, TEXT("/Script/BlueBekaTweaksFicsitFarming.BBT_FicsitFarming_CDOs"));
You should make this a UPROPERTY in your C++ mod module (which should be UCLASS(Abstract)), and then set the default value in the BP subclass Example: https://github.com/Th3Fanbus/ScrewIt/blob/master/Source/ScrewIt/Public/ScrewItRootInstance.h#L80-L85 I don't know if a class reference will work for you. If it doesn't, you can try using a soft class reference instead
BlueBeka
BlueBekaOP•2mo ago
I went with FName simply because LoadModule takes an FName. I could switch to using FString and only convert to FName last minute.
Rex
Rex•2mo ago
Oh, LoadModule Ah, that takes C++ module names Then don't switch I confused that I keep confusing C++ modules with SML mod modules
BlueBeka
BlueBekaOP•2mo ago
Thanks for all your help. I wouldn't of been able to come this far without you šŸ˜„
Robb
Robb•2mo ago
I think mod modules are supposed to be FNames and case insensitive because they correspond to folders on disk. but SMR's mod reference by URL function is case sensitive
BlueBeka
BlueBekaOP•2mo ago
I think the TPromise and using ModuleManager.OnModulesChanged() isn't actually necessary and the code can be simplified a bunch. Correct me if I'm wrong, but the dependency mod will always be loaded before the current mod and thus we don't need to handle the race condition of which mod loads first. Additionally ModLoadingLibrary->IsModLoaded(ModName) is probably better than ModuleManager.IsModuleLoaded(ModName) as the latter will only work for c++ modules.
Rex
Rex•5w ago
I think using ModuleManager.IsModuleLoaded(ModuleName) is the way to go since it checks for C++ plugin modules, which you can link against That being said, if you only do BP stuff (very likely unless you have the headers for the other mod), it probably won't matter that much

Did you find this page helpful?