#Patch Manager
1 messages · Page 3 of 1
ah i see ok
:+lfo(...) {
+plume {
}
+frost {
}
}
?
fair
Wait notating deletion is easy, we already cache every single part when we only modify a few, so we just remove what we delete from the cache?
yeah, should be as easy as that
Hmm, why isn't it letting me add a resource container, its not even getting to the json?
{
"name": "ethalox",
"capacityUnits": 0.0,
"initialUnits": 0.0,
"NonStageable": false
}
Thats an error lol
@north mist
@use 'builtin:list';
:parts {
* > resourceContainers > * {
capacityUnits: *50;
initialUnits: *50;
}
* > resourceContainers {
@if ($current:length() > 0) {
+Methalox {
capacityUnits: +100.0;
initialUnits: +100.0;
}
}
}
}
nice!!
It currently only works for resource containers ... because thats the only thing I added it for ... but
its a start
absolutely
it definitely won't be as easy as MM had it, because KSP1's config syntax was already designed to be concise and human readable, unlike the KSP 2 serialized jsons
I think custom resource definitions are going to be the first thing I make a generator adapter for
Then I can make stormlight engines :3
Munix your template is so useful for making a quick test mod
I needed to create a mod to dump all text assets
heh that's pretty much why I made it in the first place
I was making a lot of quick little test mods and patches, and it was really annoying having to set everything up
And here we go :3
I did this mostly so I could see the structure of a resource definition
{
"_comment": "flowMode is integer: 0 - NULL, 1 - NO_FLOW, 2 - ALL_VESSEL, 3 - STAGE_PRIORITY_FLOW, 4 - STACK_PRIORITY_SEARCH, 5 - STAGE_STACK_FLOW_BALANCE",
"_Comment2": "transferMode is integer: 0 - NONE, 1 = PUMP",
"_commentDescription": "Resource used for electricity",
"version": 0.1,
"useExternal": false,
"data": {
"name": "ElectricCharge",
"displayNameKey": "Resource/DisplayName/Electric Charge",
"abbreviationKey": "Resource/Abbreviation/EC",
"isTweakable": true,
"isVisible": true,
"massPerUnit": 0,
"volumePerUnit": 0,
"specificHeatCapacityPerUnit": 0,
"flowMode": 3,
"transferMode": 1,
"costPerUnit": 0,
"NonStageable": true,
"resourceIconAssetAddress": "Assets/UI/Sprites/Whitebox/WB-ICO-Battery.png",
"vfxFuelType": "NoFuel"
}
}
So inconsistent
I should add enums to SCSS ... totally ... nevermind top level variables basically being constants
@north mist I think something like the following is actually easier to introspect that it is meant to create something
@new("name")
:lfo {
}
What think you?
I do prefer it having the similar @ syntax to @delete
And its much more verbose that its creating a new thing rather than modifying something
yep
Why the fuck do resources and recipes share the same label??
Its going to be a pain to deal w/ that later
I guess maybe it's because they are both used in the same places?
though that doesn't mean they couldn't have made them load separately
you couldve just asked i have them all 😭
@north mist
awesome! now can you make it the fuel that an engine needs?
@use 'builtin:list';
@new("Stormlight")
:resources {
displayNameKey: "Resource/DisplayName/Stormlight";
abbreviationKey: "Resource/Abbreviation/SL";
isTweakable: true;
isVisible: true;
massPerUnit: 0.1;
volumePerUnit: 0.065;
specificHeatCapacityPerUnit: 2010;
flowMode: 4;
transferMode: 1;
costPerUnit: 0.8;
NonStageable: false;
resourceIconAssetAddress: "";
vfxFuelType: "Pressurized";
}
:parts {
* > resourceContainers > * {
capacityUnits: *50;
initialUnits: *50;
}
* > resourceContainers {
@if ($current:length() > 0) {
+Stormlight {
capacityUnits: +100.0;
initialUnits: +100.0;
}
}
}
}
that would pretty much already make mods like Real Fuels possible
I can see about doing that
Dont have time to continue that foray atm, will do tomorrow
@north mist
I got a swerv to run on stormlight
It requires some functional programming for it to be done, but I did a generic propellant replacer function
Id copy it here if I had some internet on my laptop
@use 'builtin:list';
@use 'builtin:dictionary';
@use 'builtin:debug';
@new("Stormlight")
:resources {
displayNameKey: "Resource/DisplayName/Stormlight";
abbreviationKey: "Resource/Abbreviation/SL";
isTweakable: true;
isVisible: true;
massPerUnit: 0;
volumePerUnit: 0;
specificHeatCapacityPerUnit: 2010;
flowMode: 4;
transferMode: 1;
costPerUnit: 0.8;
NonStageable: false;
resourceIconAssetAddress: "";
vfxFuelType: "Pressurized";
}
@function ReplacePropellants($modes, $from, $to) {
@return $modes:map(
@function($object) {
@if ($object["propellant"]["mixtureName"] == $from) {
@return $object:set("propellant",$object["propellant"]:set("mixtureName",$to));
} @else {
@return $object;
}
}
);
}
:parts {
* > resourceContainers {
@if ($current:length() > 0) {
+Stormlight {
capacityUnits: +100.0;
initialUnits: +100.0;
}
}
}
#engine_3v_hydrogen_swerv > Module_Engine > Data_Engine {
$useless: $current:debug-log();
engineModes: ReplacePropellants($value, "Hydrogen", "Stormlight");
}
#engine_3v_hydrogen_swerv > resourceSummary {
Consumes: ["Stormlight"];
}
}
How does this look munix
it's much more verbose than MM but that was to be expected, but I love how well readable this is
Better formatted
@use 'builtin:list';
@use 'builtin:dictionary';
@use 'builtin:dictionary';
@new("Stormlight")
:resources {
displayNameKey: "Resource/DisplayName/Stormlight";
abbreviationKey: "Resource/Abbreviation/SL";
isTweakable: true;
isVisible: true;
massPerUnit: 0;
volumePerUnit: 0;
specificHeatCapacityPerUnit: 2010;
flowMode: 4;
transferMode: 1;
costPerUnit: 0.8;
NonStageable: false;
resourceIconAssetAddress: "";
vfxFuelType: "Pressurized";
}
@function ReplacePropellants($modes, $from, $to) {
@return $modes:map(
@function($object) {
@if ($object["propellant"]["mixtureName"] == $from) {
@return $object:set("propellant",$object["propellant"]:set("mixtureName",$to));
} @else {
@return $object;
}
}
);
}
:parts {
* > resourceContainers {
@if ($current:length() > 0) {
+Stormlight {
capacityUnits: +100.0;
initialUnits: +100.0;
}
}
}
#engine_3v_hydrogen_swerv > Module_Engine {
$useless: $current:debug-log();
engineModes: ReplacePropellants($current, $value, "Hydrogen", "Stormlight");
}
#engine_3v_hydrogen_swerv > resourceSummary {
Consumes: ["Stormlight"];
}
}
Same its very readable
though I'm starting to think that this might be a bit more geared towards programmers than we expected
can't really see a person who was making MM configs writing this
Its mostly due to me not having some other stuff made in
I want to be able to do it w/o having the functional programming, but stuff like arrays are getting in the way
yeah that makes sense
it will require a lot of custom "adapters" as I called it, to be able to compete with MM's simplicity in any way
Plus this is the point of libraries, like the ReplacePropellants function would be abstracted into a _EngineHelpers.patch library that other patch writers can use
Thats mostly a symptom of what KSP2 does
yeah, definitely
it's not like we could have just said "let's just use MM syntax" and have it any easier than this
🙏 please add @endif
why the fuck would I do that?
more verbose 
I'll likely abstract a lot of stuff that can't be put into adapters into a general set of libraries for patch makers to use
yeah that'll definitely be helpful
People can also easily make C# extensions as well like the following is the library debug-log is defined
using JetBrains.Annotations;
using PatchManager.SassyPatching.Attributes;
using PatchManager.SassyPatching.Execution;
using PatchManager.Shared;
namespace PatchManager.SassyPatching.Builtins;
/// <summary>
/// Contains a lot of builtin debug libraries for the sassy patch engine to use
/// </summary>
[SassyLibrary("builtin","debug"),PublicAPI]
public class DebugBuiltins
{
/// <summary>
/// Logs a value into the console for debugging
/// </summary>
/// <param name="universe">The universe in which this function is being called</param>
/// <param name="v">The value to log</param>
[SassyMethod("debug-log")]
public static void Log(Universe universe, DataValue v)
{
universe.MessageLogger(v.Type == DataValue.DataType.String ? v.String : v.ToString());
}
/// <summary>
/// Serializes a value
/// </summary>
/// <param name="v">The value to serialize</param>
/// <returns>The serialized form of the value</returns>
[SassyMethod("serialize")]
public static string Serialize(DataValue v)
{
return v.ToString();
}
}
Or a type conversion library
using JetBrains.Annotations;
using PatchManager.SassyPatching.Attributes;
namespace PatchManager.SassyPatching.Builtins;
/// <summary>
/// This contains all the builtin methods for converting between types
/// </summary>
[SassyLibrary("builtin","type-conversion"),PublicAPI]
public class TypeConversion
{
/// <summary>
/// Used in a patch to convert a value to a boolean
/// </summary>
/// <param name="v">The value</param>
/// <returns>A boolean form of the value</returns>
[SassyMethod("to-bool")]
public static bool ToBoolean(DataValue v)
{
return v.Truthy;
}
/// <summary>
/// Used in a patch to convert a value to a real
/// </summary>
/// <param name="v">The value</param>
/// <returns>The value interpreted as a real</returns>
[SassyMethod("to-real")]
public static double ToReal(DataValue v)
{
if (v.IsInteger) return v.Integer;
if (v.IsReal) return v.Real;
if (v.IsString) return double.Parse(v.String);
throw new InvalidCastException($"Cannot convert value of type {v.Type.ToString().ToLowerInvariant()} to real");
}
/// <summary>
/// Used in a patch to convert a value to a real
/// </summary>
/// <param name="v">The value</param>
/// <returns>The value interpreted as a real</returns>
[SassyMethod("to-integer")]
public static long ToInteger(DataValue v)
{
if (v.IsInteger) return v.Integer;
if (v.IsReal) return (long)v.Real;
if (v.IsString) return long.Parse(v.String);
throw new InvalidCastException($"Cannot convert value of type {v.Type.ToString().ToLowerInvariant()} to integer");
}
/// <summary>
/// Used in a patch to convert a value to a string
/// </summary>
/// <param name="v">The value</param>
/// <returns>A string form of the value</returns>
[SassyMethod("to-string")]
public static string ToString(DataValue v)
{
return v.IsString ? v.String : v.ToString();
}
}
I created a custom type marshalling library for this lol
Anyways, pushed all the code I have
amazing work on this today!
But yes, with this system, a real fuels system is possible
{
"version": 0.1,
"useExternal": false,
"data": {
"name": "Stormlight",
"displayNameKey": "Resource/DisplayName/Stormlight",
"abbreviationKey": "Resource/Abbreviation/SL",
"isTweakable": true,
"isVisible": true,
"massPerUnit": 0,
"volumePerUnit": 0,
"specificHeatCapacityPerUnit": 2010,
"flowMode": 4,
"transferMode": 1,
"costPerUnit": 0.8,
"NonStageable": false,
"resourceIconAssetAddress": "",
"vfxFuelType": "Pressurized"
}
}
This is the generated and cached resource definition for stormlight as well
A language server for this would be an interesting idea lol
huh yeah that would be pretty cool
@maiden wraith this is how Cheese adds a "new config" here, which turns into JSON:
@new("Stormlight")
:resources {
displayNameKey: "Resource/DisplayName/Stormlight";
abbreviationKey: "Resource/Abbreviation/SL";
isTweakable: true;
isVisible: true;
massPerUnit: 0;
volumePerUnit: 0;
specificHeatCapacityPerUnit: 2010;
flowMode: 4;
transferMode: 1;
costPerUnit: 0.8;
NonStageable: false;
resourceIconAssetAddress: "";
vfxFuelType: "Pressurized";
}
im asking mostly for ease for modders
hmm
ok so
LFO plumes are as follows
under PlumeConfigs
the Key is the parent game Object
and inside it lies all the plumes are children of it
so basically
yeah, curves are a bitch to serialize
{
"time": 0.1,
"value": 0.9976679,
"inTangent": -2.42776752,
"outTangent": -2.42776752,
"inWeight": 0.0,
"outWeight": 0.7869052,
"weightedMode": "None",
"tangentMode": 0
},
What is your question?
well
because I'm sure they're nowhere near this long
thats the fun part
they are way smaller
because they are based ontemplates
so they just instantiate templates
but i'd like for people to also make their templates
so idk if they'd use PM for that
or wtv else
and those templates are made by hand, writing all the 2000 lines?
Unity won't generate YAML for you anyway, right?
and even if it did, human readability is not really needed or even useful here
since it is so long
nobody will be reading that
lmao

the whole idea of the YAML is for a simpler way to create templates, likke WAAAY simpler
but tbf
probably wont be used
since you cant see what ur doing
but yeah, templates in json and configs in SCSS seem nice
I need a syntax to create an unnamed object
I guess my question would be - what could you do in YAML that you couldn't do in SCSS or that would be more difficult
My biggest issue rn with writing configs in SCSS is lists, as its meant to be mostly object based, something I will have to figure out
well not much, again i was planning to, didnt even put much though it was mostly from user sugestion
I mean technically the simplest rewrite of that into SCSS is as follows (give me one minute to do it)
Just imagine that all the quotes for keys are removed (except for keys that have spaces), as quotes for keys are optional
Thats like a quick and dirty way to write that though, leveraging the fact that you can copy paste json as an object
It actually wouldn't be too hard, I mean you could write code to translate the JSON to that
I mean, let me actually write a C# script to do just that
But then again, for a case like this, where a person wouldn't be writing this by hand, there's no added benefit of doing this vs. just using JSON, I think
True, but it also does mean like this, that the config is already known to patch manager, and is added to addressables as json
But just putting it as json and registering it with patch manager also works
That's true I guess, though I'm assuming that's not implemented yet
I mean, it would get cached under whatever label lfo gives it with the name BE-4
Yeah got it, I was thinking more so in the general :json case
Just LFO would have to start reading its configs from addressables if that were the case
That would be the easiest to implement
@new("label","name")
:json {
@set "copy and pasted json goes here";
}
would be so easy to implement, I just haven't gotten around to that
I need to stop mucking around with the app bar and finally finish it so that I can get back to actually being helpful here and in SpaceWarp
Just to verify for my own sanity.
yall are making this with the ability to use it fully programmatically as well if a modder wanted to and would not NEED to add things via a json file?
I'm trying to parse this
1st off, this is modifying/adding json files to the addressables, so based off of that you would need to use a json file already built into an addressables bundle to add stuff
barring that, the interface for generating text assets is exposed, so you could just implement that and register a bunch of those for that purpose, but the end result will be in memory JSON, as thats what the game wants from the addressables bundles
3rd off, this isn't a JSON file, its a patch file that has a built in turing complete functional programming language should the need arise to use that
But what exactly do you mean?
So I am coming from games that we didn't really HAVE official mod support so we had to make everything so some things I may ask about be in existence in the games code itself and if so please bear with me as I have yet to fully explore it 🙂
hrmmmmmm how to even phrase this......
In subnautica mods would have kind of a Patching phase where they would send all the changes all the mods wanted to make to the game into our SMLHelper library.
https://github.com/SubnauticaModding/Nautilus/blob/master/Nautilus/Handlers/CraftDataHandler_Subnautica.cs
Then they would be stored and organized and then SMLHelper would in the end do the patches to the game to help with mod compatability.
https://github.com/SubnauticaModding/Nautilus/blob/master/Nautilus/Patchers/CraftDataPatcher_Subnautica.cs
This way mods that made changes via a json esk interface called CustomCraft2 would load all the text based assets and send them to SMLHelper and mods would use the same methods but directly through referencing SMLHelper which allowed for text based mods and full programmatic mods to live and work in sync.
Does Patch Manager have a central DB of all changes mods of both types make or will it only be for loading from text files and other mods will live in the wild west where a bunch of mods break every time the game code changes because everyone is patching on their own instead of going through one central tool that is the only thing that really needs fixing each update
tbh I am probably asking things way too soon before seeing how the modding actually works for the game lol
problem is everytime I try and find resources for how to mod both these games its all over the place lol
so i am just kinda try and figure out what is actually happening still
Patch Manager is basically meant to be the spiritual successor of KSP1's Module Manager, its core functionality is simply loading the game's JSON files, running user-made patches on them, caching them, and then providing them for the game to load them
As for an actual modding API, that's SpaceWarp
ok so spacewarp is kinda like our smlhelper?
Maybe? Not sure, don't really know enough about it to say for sure, but SpaceWarp is basically an abstraction layer over BepInEx/the game's internal mod loader, adding some QoL stuff and common APIs for mods to interact with the game systems
For example it simplifies the loading of asset bundles, addressables, soundbanks, localizations, etc., has APIs to add mod buttons to the game's toolbars, adds mod settings to the game's Settings page, adds support for asset-only mods with no DLLs (such as parts), Lua mod support, in-game console, mod list UI, mod version checking, and more
then yup its smlhelper KSP2 edition 🙂
I think I should work on the generic json creator next, it should be quite simple
Quick question: is it currently possible or is it planned in the future to be able to use other variables when defining values in the patch, something like this:
@use 'builtin:list';
:parts {
* > resourceContainers > * {
capacityUnits: ${initialUnits}*50;
}
}
?
@use 'builtin:list';
:parts {
* > resourceContainers > * {
capacityUnits: $current["initialUnits"]*50;
}
}
$parent also exists, anything more, and you need to set a custom variable for that
Though there is a case to be made about shorthanding stuff on the same object, maybe making $$... shorthand for $current[...]? or a more intuitive shorthand
Also, you don't need builtin:list for that patch, but thats beides the point
@north mist what do you think?
Yeah I just copy-pasted something from above in this thread without thinking about it much x)
Oh yeah that's definitely a good idea
Consider it done then :3
Alright so now @stiff crater that would be more like
@use 'builtin:list';
:parts {
* > resourceContainers > * {
capacityUnits: $$initialUnits*50;
}
}
Which also means that this patch
:parts {
@if $current["crewCapacity"] > 0 {
crewCapacity: +10;
}
}
can become
:parts {
@if $$crewCapacity > 0 {
crewCapacity: +10;
}
}
Though it could also be
:parts {
crewCapacity: $value+10 @if $value > 0 @else 0;
}
@upbeat hare how would we go to copy a sound?
:"SPT-100"{
engineSound.Start = ["dawn"].engineSound.start
}
how are the sounds represented in the JSON?
nop
well
cuz AKWise
tbf its far nicer to mess with that in Unity
cuz the integration with AKWise is amazing
also, its not serialized cuz its not a module or in the partCoreData
its on KSPPartAudio
could we make a custom module that would have the information needed?
sure
that would make things much easier
the module could be a part of SpaceWarp
hmm sure
since playing sounds seems like very basic functionality
tho this will make savefiles bigger
we even have a SpaceWarp.Sound assembly already
cuz you'll save every sound on the save json
each part has like 7+
engines have like 10+
does every single property of every single part module get serialized into the save file?
if we want to modify it
it has to
the KSPState gets saved in the save file
the KSPDefinition gets saved (and read) from the parts_data json
so, either we add it with KSP2 Unity Tools
and save it on the parts_data json
I'm not sure what that means
they are attributes that you put on fields and properties
remember that u were like "why isnt this read from the parts_data json"
it decides where to read things from based on those 2 attributes
So lets say you have a field foo and a field bar
[KSPState]
foo = 1;
[KSPDefinition]
bar = false;
void OnOABModuleStart(){
bar = true;
foo = 2;
}
//bar will be overwritten everytime a new save is loaded, it reads the value on the parts_data json
//
//foo will be saved on the savefile
//
//So save file will be
//{
// "foo" = 2
//}
it also saves every value w/o a attribute iirc
so we have 2 options
make the list of AKEvents (what defines what sound is played) a KSPDefinition
actually
we can only have KSPState
how come?
we only have access to the sounds after the prefab is loaded
which is only if
A - it is spawned on the OAB
B - it exists in a vessel
so lets say you want to get a sound event from something that wasnt loaded yet, you'd have to load it yourself (which takes time)
and we can't really add the Module to existing parts since we dont have them on the parts in unity
consequentially, we cant have all the sounds be in the parts_data
unless, we load all prefabs when the game loads all parts data
which will add a lot of time to the loading
what we could do is have a list of all the events
in the wiki
and use the Module as an interface
so, with PatchManager, you'd add the Module and do something like
+Module_EngineSound{
.Start = "eventID"
}
and then the module would add it
but i think its easier to just have a Sound Manager than add a whole module for that
the right way to do it tho, requires people to have AKWise Installed
but that doesn't solve the issue of trying to copy stock part sounds
it does
how?
you're saying the right way
or the Sound MAnager way?
both solve it, but diffrently
this I mean
what i did, and what people would have to do
is have an empty Event in AKWise
with the same name
either we provide an AKWise project with all events, or we provide all event names in the wiki
Can we not programatically make an empty event?
huh
I feel like this will be another huge obstacle for people trying to move from KSP1 modding to KSP2 modding
we dont need to
thats the other solution, the SoundManager would do AKWise.GetEvent("eventID")
and add that to the KSPPartAudio.AudioEvents[n]
so no need to create an empty event
I feel like this should definitely be something that people don't have to create a DLL for
as for, create an empty even in unity, w/o the AKWise as a middle man, not sure
so I am for this sort of solution
they wouldnt need for the Sound Manager
the idea is to have a config where people would put the ids and what events it corelates
and Sound MAnager would read that
thus my idea of making that in PM
yeah that's what I'm saying
Oh yeah sorry missread
tho, im not sure how we'd do this since its not json
thus me asking Cheese for her guindance
i mean sure it can be yeah
i was thinking of a "fileless" config
as in, its all on the PM patch file
but not sure how we'd do that
I mean, all that PM does is either modify or create JSON files
then create once it reads the patch?
that sounds fine to me
or better, cache it, instead of creating and leaving there
well that's how PM works
Of course it caches it lol
it caches everything
Itll be in the addressables system afterwards ... though I have an idea to address that and make stuff be able to be sent to an abstract target
Sound Manager, would it be a separate mod or a SpaceWarp module?
but also, is there any reason why a separate PM/JSON config would be better than a part module?
since this has to be tied to specific parts anyway
and I feel like modules are the best way to do that
Modules are more for something thats changeable between instances of the same part
the sound event is the same for every part instance
Oh gosh that reminds me that I have to figure out part modules
in Patch Manager
either way
Oh
thats diffrent
also, dont forget that Modules are on a Update Loop
Adding to the json isnt enough iirc
yup, you need to add to the prefab too
and set whats needed
but that'd be my job in PM
at least what i intended to do xD
I mean just patch where it loads parts to introspect the json and add/remove modules there
i just need it to be able to create stuff with PM
the prefab is not loaded at that stage
if you do that
you'd be loading every prefab
which would add a good 30s of loading
or even more
just with stock parts
Well then whenever the prefab is loaded introspect the json
#1115274490490929172 message
but then comes the problem that munix faced
how would you know what gameobject is the gimbal, thrust transform
etc
its not on the json
my idea is to have something like a class that stores extra information somehow
and you can just info["gimbalTarget"]
by just making the json store the name of the object 🤷♂️
parts_data json?
I know that's overly simplified
or the cached PM json?
yeah
parts data, we'll have the same issue as the sounds
we cant really modify the stock json
why not? we could technically patch the serializer and store the extra info without modifying the target classes
at least i dont think you could add a new field
Not as easy as you think
I mean I'm not saying any of this is easy
can't we patch the places in code where parts are deserialized and use a custom serializer extension?
not reliably
cuz of the attributes
we could even have the extrainfo stored on a PM Class and go like PatchManager["partData"]["gimbalTarget"]
we'd have to have a way to discern extra info from normal json info
ok but where would that info come from?
maybe a special char Cheese?
if not from parts_data
Huh?
from the patchmanager patches
[Raptor]{
+Module_Gimbal{
.gimbalRange = 15
%extra-info
.gimbalTarget = gimbalTransform
}
}
something like this
idk if it works for SCSS
either we could just check with the class if it exists
if it doesnt then add to extrainfo
I mean ... but where would that even go?
as in be stored?
my idea is either a static class
or a cache json file
so like Dictionary<string, Dictionary<string,object>> PatchInfo.extraInfo
1st string being partName (or some other identifier) and 2nd string being the scss field name
we really should have this extra info to allow for more flexibility
else we're limited to only whats on the json
which isnt much
idk, I still don't like this very much
I'd rather try to patch the stock classes using Mono.Cecil to add custom fields to the parts_data
you can also very easily add attributes or anything with it
and you wouldn't need to patch the serializer or anything
i think thats overcomplicating it
and ie if a modders wants to add more info
it would also be a hassle for them
I feel like the other approach is much more complicated than this, but idk
if modders want to add more info to their parts, I still feel like they should make custom modules
we're talking about basically adding prefab info to the json, that's different
heh
idk i dont like the idea of messing with the stock json structure
or even
the classes
it would be much closer to MM
The ksp2 devs atent making this easy on us
i mean to me the prefab-json system makes perfect sense
yeah, that's definitely one thing we can agree on
Honestly though, have an extra module that on start sets the transforms and stuff of other modules then promptly kills itself
I mean sure, to you
but you're not a KSP 1 modder
not sure if you can just kill a module
i think it gets registered in a couple of update things
and saved on partmodulestate
but yeah, that was my initial idea
i mean, theres not much diffrence between
.gimbalTarget
//and
%extra-info
.gimbalTarget
since it seems to be the closest to the intended way of "modding"
also the tag probably isnt even needed
as we can just check with the data class if the field exists
to the PM user it will be no diffrence
This seems quite convoluted
Having a module meant to modify other modules is quite simpler, and if we can just put all modifications on a single module it wont be too bad
hmm
that reminds me of this
is there no way to specify in the module which fields of it should be part of the state?
well module's cant target everything, but i think everything else can be targeted other ways by PM
actually
the module wont work i think
yes, via the attributes
but modules wont work cuz
lets say you want to add a gimbal to the engine
you'd have to initialize your module before the Module_Engine
which im not sure its possible
maybe if you re-order the modules
to make your module first
i had that issue with the B9PS
why is that? can't the Module_Engine be updated retrospectively?
Isnt there a scriptexecutionorder attribute
nop
i mean theres on unity
by default
but idk how you'd change that
depending on what ur changing no
i mean you could call Initialize() again
but most of the modules who have required fields fail if something missing
and makes part look like what SPT100 was
but its not from that
if you do it on Start or Awake
it ownt have the part set
if you do on Initialize(), the order depends on the module's list order
and if a part fails during initialization it looks like this in flight
DefaultExecutionOrder but it seems moot
like this in oab
and the same in flight
and im not sure if you can just "revert" this
or fix, w/o reloading the whole part
execution order wont work, the order is not set by unity, its set by a list
it does a foreach on the list
you can reorder the list tho
but then you cant set the engine's gimbal since it hasnt intialized yet
but if you do after initialize it will have failed
your best bet is to patch the initialize() method of the engine
or set it before initialization, somehow
you could maybe do it on CreatePart()
what is PartBehaviorModule.ExecutionPriorityOverride?
Not initialized, but it exists right?
not on the gameobject
idk, let me check
pretty sure that's what we need
yeah, + FixedUpdate
They call initialize and add the module at the same time?
Surely we can split that one out with a patch right?
let me check
but iirc they do AddModule then Initialize
on the same method
ok so
if you want to do it that way
you'd have to ovewrite the MergePartModuleData
which would overwrite the information on the save
basically
thats the point where it has every module and its not initialized
but if what you change has a [KSPState] attribute, it will be overwritten
not sure if you can update it on a postfix
PartComponentModule partComponentModule = Activator.CreateInstance(serializedPartModule.ComponentType) as PartComponentModule;
cuz it creates the instance in the middle of it
I wish there was just like a "OnInitialized" event that we could subscribe to for the gimbal module
and just update its fields in that
theres no subscribeable events
yeah I know
but you can just postfix the Initialize
I mean an ilmanipulator would work would it not?
you could try
actually
you'd have to do it a bit further down
private void MergePartModuleData(List<SerializedPartModule> modifiedPartModules)
{
this.Modules = new DictionaryValueList<Type, PartComponentModule>();
PartComponent.MergePartModuleData(base.Name, modifiedPartModules);
foreach (SerializedPartModule serializedPartModule in modifiedPartModules)
{
PartComponentModule partComponentModule = Activator.CreateInstance(serializedPartModule.ComponentType) as PartComponentModule;
if (partComponentModule != null)
{
partComponentModule.SetPart(this);
foreach (SerializedModuleData serializedModuleData in serializedPartModule.ModuleData)
{
partComponentModule.DataModules.Add(serializedModuleData.DataType, serializedModuleData.DataObject);
if (this.initialDefinitionData != null && !((PartDefinition)this.initialDefinitionData).OriginalPartId.IsDefault())
{
partComponentModule.DataModules[serializedModuleData.DataType].UpdateGuids(((PartDefinition)this.initialDefinitionData).OriginalPartId, base.GlobalId);
}
}
this.Modules.Add(serializedPartModule.ComponentType, partComponentModule);
}
}
}
you'd maybe have to do it on the 2nd foreach
huhhhhhhhhh
we have yet another problem
prefab not loaded
gimbal target is a transform
how would you to a GetChild(gimbalTarget)
if you dont have the gameObject?
Wait if the prefabs not loaded ... its not getting the transforms in initialize?
The partbehaviour module is what has the transform for gimbal ... got it
yes
But the transformname is in the gimbal data
well this is more of an example, if you really want to try this, do it with thrust transforms for Engines
this.gimbalTransforms = new List<Transform>(base.part.FindModelTransforms(this.dataGimbal.gimbalTransformName));
cuz gimbal is gotten via just name
Again. .. in the data
thrust transforms are transform references
All the thrust transforms are in the data though
private void SetThrustTransforms()
{
if (base.PartBackingMode == PartBehaviourModule.PartBackingModes.Flight && this._engineForces != null)
{
for (int i = 0; i < this._engineForces.Count; i++)
{
base.part.RemoveForce(this._engineForces[i]);
}
}
if (this.currentEngineModeData.ThrustTransformNamesMultipliers == null || this.currentEngineModeData.ThrustTransformNamesMultipliers.Length == 0)
{
if (base.PartBackingMode == PartBehaviourModule.PartBackingModes.Flight)
{
List<Transform> thrustTransforms = new List<Transform>(base.part.FindModelTransforms(this.currentEngineModeData.thrustVectorTransformName));
this.currentEngineModeData.ThrustTransforms = thrustTransforms;
}
else
{
List<Transform> thrustTransforms2 = new List<Transform>(base.OABPart.FindModelTransforms(this.currentEngineModeData.thrustVectorTransformName));
this.currentEngineModeData.ThrustTransforms = thrustTransforms2;
}
}
else
{
this.currentEngineModeData.ThrustTransforms = new List<Transform>();
this.currentEngineModeData.ThrustTransformMultipliers = new List<float>();
for (int j = 0; j < this.currentEngineModeData.ThrustTransformNamesMultipliers.Length; j++)
{
if (base.PartBackingMode == PartBehaviourModule.PartBackingModes.Flight)
{
Transform item = base.part.FindModelTransform(this.currentEngineModeData.ThrustTransformNamesMultipliers[j].ThrustTransformName);
this.currentEngineModeData.ThrustTransforms.Add(item);
}
else
{
List<Transform> collection = new List<Transform>(base.OABPart.FindModelTransforms(this.currentEngineModeData.ThrustTransformNamesMultipliers[j].ThrustTransformName));
this.currentEngineModeData.ThrustTransforms.AddRange(collection);
}
this.currentEngineModeData.ThrustTransformMultipliers.Add(this.currentEngineModeData.ThrustTransformNamesMultipliers[j].ThrustTransformMultiplier);
}
}
this._engineForces = new List<Data_Engine.EngineForce>(this.currentEngineModeData.ThrustTransforms.Count);
this._engineShockForces = new List<Data_Engine.ShockwaveForce>(this.currentEngineModeData.ThrustTransforms.Count);
this._engineDamageForces = new List<Data_Engine.DamageForce>(this.currentEngineModeData.ThrustTransforms.Count);
this._thrustCollisions = new List<Data_Engine.ThrustCollisionInfo>(this.currentEngineModeData.ThrustTransforms.Count);
for (int k = 0; k < this.currentEngineModeData.ThrustTransforms.Count; k++)
{
this._engineForces.Add(new Data_Engine.EngineForce());
if (base.PartBackingMode == PartBehaviourModule.PartBackingModes.Flight)
{
base.part.AddForce(this._engineForces[k]);
}
this._engineDamageForces.Add(Data_Engine.DamageForce.Default);
this._engineShockForces.Add(Data_Engine.ShockwaveForce.Default);
this._thrustCollisions.Add(new Data_Engine.ThrustCollisionInfo());
}
this.InitThrustTransformMultipliers();
if (this.currentEngineModeData.ThrustTransforms.Count <= 0)
{
GlobalLog.ErrorF(LogFilter.Flight, "Engine {0} has no thrust transforms defined/found!", new object[]
{
base.part.SimObjectComponent.PartName
});
}
}
See all the this.currentengineModeData.ThrustTransformNames
oh but
No ... its a string
Why?
it will cover 99%
why is it limited
cuz it doesnt allow for diffrent names
huh?
Though I see one issue ... the engine class contains references to the module gimbal and module alternator
thats another isse
But thats a much easier solved issue
nah its already solved
Thats the one ... which is so odd that every other component does it from the json
i think cuz
its the only one that has oposing transforms
every other has either just 1 transform
or all in the same direction
so name match would be a pain in the A for devs
since they couldnt name the transforms acording to their direction
Imma be honest that explanation makes no sense
They could still name the transforms
How so?
Debug.Log($"{gameObject.Name} applied n kN of force")
That makes no sense that that wouldn't still work if the transforms were by name
Because by that point you already name matched and grabbed the transforms
No they would not be named the same
ok i get it
I mean yes, the engine uses one string, but its like so goddamn easy to just add a list of strings to the json, I dont understand the devs here
But is this really that important ... as to change the transform you already have to have a different prefab ... which would already have the transform set there
But wouldn't the part need to already be modelled and built with that in mind
sure
but if we're doing this
for ksp1 modders
they'll want to be able to do it on the config
thats the MM way
I don't think that's what we were talking about here at all
making parts without Unity I mean
this is just about patching existing parts
And most modules people will add will be made by modders which hopefully will do string matching
Im still not entirely sure its possible to do a full part without unity
a copied one yeah
you could also patch the prefab loading and create one from 0
tho its a lot of work
either way
another problem
animations
Module_Deployable uses them
That feels out of scope
heh fair
Hmm, I think if I add the ability to add modules to parts and to define recipes, I think we can do a prerelease
Adding modules is going to definitely be the fun part of that
I pushed the ability to add recipes and modify recipes
{
"Name": "PartComponentModule_Engine",
"ComponentType": "KSP.Sim.impl.PartComponentModule_Engine, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"BehaviourType": "KSP.Sim.impl.PartComponentModule_Engine, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"ModuleData": [
{
"Name": "Data_Engine",
"ModuleType": "KSP.Modules.Module_Engine",
"DataType": "KSP.Modules.Data_Engine",
"Data": null,
"DataObject": {
"IndependentThrottle": {
"ContextKey": null,
"storedValue": false
},
"IndependentThrottlePercentage": {
"ContextKey": null,
"storedValue": 0.0
},
"EngineModeString": {
"ContextKey": null,
"storedValue": ""
},
"activeEngineMode": {
"ContextKey": null,
"storedValue": "default"
},
"EngineStatePriorChangeMode": "Off",
"EngineChangingToMode": 0,
"EngineAutoSwitchMode": {
"ContextKey": null,
"storedValue": true
},
"thrustPercentage": {
"ContextKey": null,
"storedValue": 100.0
},
"FinalThrustValue": 0.0,
"RealISPValue": 0.0,
"StatusString": {
"ContextKey": null,
"storedValue": "Nominal"
},
"StatusISPString": {
"ContextKey": null,
"storedValue": 0.0
},
"stagingOn": {
"ContextKey": null,
"storedValue": true
},
"staged": false,
"Flameout": false,
"EngineIgnited": false,
"EngineShutdown": false,
"HeatProduced": {
"ContextKey": null,
"storedValue": 0.0
},
"currentThrottle": 0.0,
"thrustCurveDisplay": 1.0,
"thrustCurveRatio": 1.0,
"EngineSpool": 0.0,
"ThrustDirRelativePartWorldSpace": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"currentEngineModeIndex": 0,
"engineModes": [
null
],
"UseEmissive": false,
"EmissiveMaterialNames": null,
"EmissiveTemperatureCurve": null,
"EmissiveLerpRateUp": 0.1,
"EmissiveLerpRateDown": 0.01,
"DeployedModeAnimationStateShortName": "OPENED",
"ModuleType": "KSP.Modules.Module_Engine, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"State": "Off",
"IsOperational": false,
"DataType": "KSP.Modules.Data_Engine, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"IsActiveInStagingProp": {
"ContextKey": null,
"storedValue": false
},
"$type": "KSP.Modules.Data_Engine, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
}
}
]
}
Am able to add a module to the json definition nonw
Just need to fix one thing
This works with custom made modules too?
It should yes, its all reflection based and looks over every assembly
Nice!
I just need to next figure out how to instantiate a module
And there we go fixed the behaviour type being wrong
Did you actually put the new version in
I mean I keep running DeployAndRun
but I can try manually
smh
ahhh why is the building broken
it doesn't copy files into the dist folders as it should
no it does, but the wrong files
what in the hell
alright, no clue what was happening, but clearing the build and dist folders and doing a nuget restore and rebuild worked
now the game runs with PM again
Ive gotta say i didnt know there was a deployandrun option for pm
Ive been manually copying everything
Yeah it was built on top of the template 😆
I moved the PatchManager.Resources project into src, for some reason it was the only project outside of it
Likely cuz I forgot to check that in Rider
@maiden wraith where are modules initialized again?
a sec
depends
they have a couple of initialize steps
what are you trying to do?
I want to add extra modules to the prefab as its being loaded
oh
then just use the same 2 patches that ColorsPatch uses
that way it will apply to all instances of the prefab
and you'll only apply 1 per saveload
or ur trying to avoid patches?
[HarmonyPostfix]
[HarmonyPatch(typeof(ObjectAssemblyPartTracker), nameof(ObjectAssemblyPartTracker.OnPartPrefabLoaded))]
internal static void ApplyOnGameObjectOAB(IObjectAssemblyAvailablePart obj, ref GameObject prefab)
{
}
[HarmonyPostfix]
[HarmonyPatch(typeof(SimulationObjectView), nameof(SimulationObjectView.InitializeView))]
internal static void ApplyOnGameObjectFlight(GameObject instance, IUniverseView universe, SimulationObjectModel model)
{
}
you can get the part name on the obj and model specifically
I'm trying to figure out where I can add modules to the prefab based on the json
thats the best place
i dont think theres a method that has both the json and the prefab
with those 2, you can, with the partname, get the json
When is the json data applied to the prefab?
MergeModuleData
a sec
let me see where that is
but
thats only Modules' data, part core data is applied before that
PartComponent.MergePartModuleData
not sure if you have acess to the right gameObject there tho
Alright, I mean, if it adds the data modules there
Wait so there is
PartBehavior for the component describing a part
and
PartBehaviourModule for a modules base class
why does a u
How do I get the Part and GameObject from the second one?
internal static class PartModuleLoadPatcher
{
[HarmonyPostfix]
[HarmonyPatch(typeof(ObjectAssemblyPartTracker), nameof(ObjectAssemblyPartTracker.OnPartPrefabLoaded))]
internal static void ApplyOnGameObjectOAB(IObjectAssemblyAvailablePart obj, ref GameObject prefab)
{
foreach (var behaviourType in obj.PartData.serializedPartModules.Select(module => module.BehaviourType))
{
if (prefab.GetComponent(behaviourType) == null)
{
prefab.AddComponent(behaviourType);
}
}
foreach (var component in prefab.GetComponents<PartBehaviourModule>())
{
var t = component.GetType();
if (obj.PartData.serializedPartModules.All(x => x.BehaviourType != t))
{
Object.Destroy(component);
}
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(SimulationObjectView), nameof(SimulationObjectView.InitializeView))]
internal static void ApplyOnGameObjectFlight(GameObject instance, IUniverseView universe, SimulationObjectModel model)
{
var part = model.Part;
foreach (var behaviourType in part.PartData.serializedPartModules.Select(module => module.BehaviourType))
{
if (instance.GetComponent(behaviourType) == null)
{
instance.AddComponent(behaviourType);
}
}
foreach (var component in instance.GetComponents<PartBehaviourModule>())
{
var t = component.GetType();
if (part.PartData.serializedPartModules.All(x => x.BehaviourType != t))
{
Object.Destroy(component);
}
}
}
}
@maiden wraith do you think something like this will work?
hmm
probably
im thinking
if there's references before that that you'd need to add
but im not remembering any
Alright so it added the part module to the json
now is the time to do the real thing
@maiden wraith any insight?
AHh, wait, I should probably put the fully qualified names in there
Now I'm confused
What is it?
{
"Name": "PartComponentModule_Test",
"ComponentType": "TestModule.PartComponentModules.PartComponentModule_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"BehaviourType": "TestModule.Modules.Module_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"ModuleData": [
{
"Name": "Data_Test",
"ModuleType": "TestModule.Modules.Module_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"DataType": "TestModule.PartDataModules.Data_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Data": null,
"DataObject": {
"TestData": "coupler_1v_inline_2point",
"ModuleType": "TestModule.Modules.Module_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"DataType": "TestModule.PartDataModules.Data_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"IsActiveInStagingProp": {
"ContextKey": null,
"storedValue": false
},
"$type": "TestModule.PartDataModules.Data_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
}
}
]
}
"Name": "PartComponentModule_Test",
public class Data_Test : ModuleData
{
public override Type ModuleType => typeof(Module_Test);
public string TestData = "DEFAULT";
}
is how Data_Test is defined
UGH
i hate the way that json puts {}
no yeah the json is right
what i originally thought
I mean it represents JavaScript objects, and those have to be contained in {}

the problem is that
"ABC" : {
//
}
"EFG" :
{
//
}
Wait, first do I have to put serializable on my classes?
what's the problem?
thats right
Data has to be but i dont think thats the problem
the problem is that its getting the wrong type reference from what i can tell
{ "abc": {}, "efg": {}} represents a JS object which has two properties, "abc" and "efg", and the values of both of those are empty objects
How can I fix this?
the brackets placement
And why does it not break with say SORRY
I still don't understand, where else should they be? 😅
but anyway
not the point of this chat, sorry
Hmm, it seems usually that the assembly qualified name here doesn't have culture or version?
And why does it matter if its missing references ... isn't that the point of the assembly qualified name?
why the hell is it inconsistent in the stock configs
I'm gonna quickly try with a stock module
pls do
No wait, it has the same error
{
"Name": "PartComponentModule_Decouple",
"ComponentType": "KSP.Sim.impl.PartComponentModule_Decouple, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"BehaviourType": "KSP.Modules.Module_Decouple, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"ModuleData": [
{
"Name": "Data_Decouple",
"ModuleType": "KSP.Modules.Module_Decouple, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"DataType": "KSP.Modules.Data_Decouple, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"Data": null,
"DataObject": {
"EjectionImpulse": {
"ContextKey": null,
"storedValue": -1.0
},
"isDecoupled": {
"ContextKey": null,
"storedValue": false
},
"ejectionForce": 10.0,
"IsStageable": true,
"isOmniDecoupler": false,
"automaticDir": true,
"explosiveNodeID": "top",
"anchorName": "",
"ModuleType": "KSP.Modules.Module_Decouple, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"DataType": "KSP.Modules.Data_Decouple, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"IsActiveInStagingProp": {
"ContextKey": null,
"storedValue": false
},
"$type": "KSP.Modules.Data_Decouple, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
}
}
]
}
I added this module to every part and it came out with the same error
Yes
Wait ... could it be that the $type is at the end?
I swear to whatever fucking god there is, why the fuck
huh the order in a JSON file shouldn't matter, should it?
depends
of properties I mean
actually, it shouldnt matter yeah
OHHHHHHH
i think i see
they have a custom deserializer
that goes by token, in order
it can be this
WHY
YOU ARE FUCKING KIDDING ME, SWITCHING THAT AROUND CAUSED THE ERROR TO DISAPPEAR
what are you guys using?
YOU WILL BE ONE OF US
I still don't see my module in the PAM though
probably some illegal substances at this point
is there anything in the PAM?
wait, what modiue?
Yes
empty modules dont apear on the PAM
public class Module_Test : PartBehaviourModule
{
public override Type PartComponentModuleType => typeof(PartComponentModule_Test);
public override void AddDataModules()
{
base.AddDataModules();
DataModules.TryAddUnique(_dataTest, out _dataTest);
}
public override string GetModuleDisplayName() => "Test";
public override void OnInitialize()
{
_test = new ModuleAction(Test);
_dataTest.AddAction("Test", _test);
var isVisible = base.part != null;
_dataTest.SetVisible(_test, isVisible);
}
private void Test()
{
TestModulePlugin.Instance.SWLogger.LogInfo($"Ran module test w/ string {_dataTest.TestData}");
}
private Data_Test _dataTest;
private ModuleAction _test;
}
and this is empty
im not sure
Does adding the action not actually do anything?
_test is a valid module Action
actually
Module Aciton
check the Action Manager
the one where u set custom engine activate and deactivate etc
thats what ModuleActions are
PAM things are ModuleProperty
just change
on the
wait easier
i'll write it for u a sec
No, hmm, the setvisible function does eventually do stuff with the pam
Wait, the error didn't disappear, I'm dumb
I just broke Patch Manager
public override void OnInitialize()
{
var moduleProperty = new ModuleProperty<string>("Hey!");
_dataTest.AddProperty(moduleProperty, "Test");
-dataTest.SetVisible(moduleProperty);
}
LMAO
[Error :Patch Manager] Could not run patch: C:\Program Files (x86)\Steam\steamapps\common\Kerbal Space Program 2\BepInEx\plugins:add_test.patch due to: 6:8: msg
it should 🙄
i mean, dont you generate token definitions
it should at least warn you the token is missing
The error messages are something I need to make better for a full release, after prerelease
Anyways, the error still magically went away
such is programming
{
"Name": "PartComponentModule_Test",
"ComponentType": "TestModule.PartComponentModules.PartComponentModule_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"BehaviourType": "TestModule.Modules.Module_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"ModuleData": [
{
"Name": "Data_Test",
"ModuleType": "TestModule.Modules.Module_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"DataType": "TestModule.PartDataModules.Data_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Data": null,
"DataObject": {
"$type": "TestModule.PartDataModules.Data_Test, TestModule",
"TestData": "adapter_2v_conical_1v-2v",
"ModuleType": "TestModule.Modules.Module_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"DataType": "TestModule.PartDataModules.Data_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"IsActiveInStagingProp": {
"ContextKey": null,
"storedValue": false
}
}
}
]
}
The literal only difference is where $type is
that is so stupid
tbh
it wasnt on the correct order
how did you expect for json to work properly?
🙄
did the module property work?
truly
it's like saying that
class A
{
public int X;
public int Y;
}
and
class A
{
public int Y;
public int X;
}
should work differently

Didn't fully test that, I think I need a little bit of unity explorer though
who does that
Will this work on already in world vessels @maiden wraith
And are you sure that those places are where I should hook?
wym? those 2 patches?
yes
they are the 1st place where the prefab goes through after being loaded
thats the earlier that you can add smth to the prefabs
unless you want to do some il patching
il wizardry
@maiden wraith the behaviour isn't being added to the prefab
is it still the same pathc as the one you sent a few minutes ago right?
Yes
add a debug on inside the == null
cuz
thts the same patch that i have on Patchouli
and it correctly adds Module_Variant to everything
even if it doesnt have it on the json
[HarmonyPatch(typeof(ObjectAssemblyPartTracker), nameof(ObjectAssemblyPartTracker.OnPartPrefabLoaded))]
public static void Prefix(IObjectAssemblyAvailablePart obj, ref GameObject prefab)
{
if (VariantManager.PartNameHasVariants(obj.Name) || VariantManager.TagListHasVariants(obj.Tags.Split(' ')))
{
var moduleVariant = prefab.AddComponent<Module_Variant>();
Data_Variant data = new();
data.SetPartName(obj.Name);
moduleVariant.dataVariant = data;
((OABPartData)obj).PartData.serializedPartModules.Add(new SerializedPartModule(moduleVariant, true));
}
}
the last line is bcuz it isnt on the json
Its just not even showing that the patch is running
Wait, why did you give me it as a post fix @maiden wraith
But yeah, I have all thes debug logs, and not a single one gets called
cuz its what i use 
well not for parts manager
but for
wait

the OAB has to be a Pre and the Flight a Post
my bad
ma'am
But also ... even then the flight just isn't being called
I'm testing only flight
Like I'm loading straight into a save that has an inflight vessel
well thats wierd
cuz its the same patch that SW uses
and patchouli too
and im using patchouli rn on the same thingie
straight to inflight
Nothing in OAB either, hmmm...
[LOG 22:18:43.390] ApplyOnGameObjectOAB - parachute_1v beginning patch
[LOG 22:18:43.394] ApplyOnGameObjectOAB - parachute_1v testing KSP.Modules.Module_Drag
[LOG 22:18:43.396] ApplyOnGameObjectOAB - parachute_1v testing KSP.Modules.Module_Parachute
[LOG 22:18:43.398] ApplyOnGameObjectOAB - parachute_1v testing KSP.Modules.Module_Color
[LOG 22:18:43.399] ApplyOnGameObjectOAB - parachute_1v testing TestModule.Modules.Module_Test
[LOG 22:18:43.401] ApplyOnGameObjectOAB - parachute_1v adding TestModule.Modules.Module_Test
[LOG 22:18:43.403] ApplyOnGameObjectOAB - parachute_1v checking KSP.Modules.Module_Drag
[LOG 22:18:43.405] ApplyOnGameObjectOAB - parachute_1v checking KSP.Modules.Module_Parachute
[LOG 22:18:43.406] ApplyOnGameObjectOAB - parachute_1v checking KSP.Modules.Module_Color
[LOG 22:18:43.408] ApplyOnGameObjectOAB - parachute_1v checking TestModule.Modules.Module_Test
Well its trying to add to the OAB
but now its making everything purple
at KSP.Sim.Definitions.ModuleDataList.TryAddUnique[T] (T addData, T& resultData) [0x00000] in <dc2cf65ae9984a6cb5c1425a7dbf267d>:0
at TestModule.Modules.Module_Test.AddDataModules () [0x00008] in C:\Users\arall\SpaceWarp Mods\TestModule\src\TestModule\Modules\Module_Test.cs:13
at KSP.Sim.Definitions.PartBehaviourModule.Init () [0x0006d] in <dc2cf65ae9984a6cb5c1425a7dbf267d>:0
at KSP.OAB.ObjectAssemblyPart.FinalizeModules (UnityEngine.GameObject newObject, KSP.OAB.IObjectAssemblyAvailablePart availablePart) [0x0023b] in <dc2cf65ae9984a6cb5c1425a7dbf267d>:0
at KSP.OAB.ObjectAssemblyPart.FinalizeLoad (KSP.OAB.ObjectAssemblyBuilderEvents events, KSP.OAB.IObjectAssemblyAvailablePart part) [0x00073] in <dc2cf65ae9984a6cb5c1425a7dbf267d>:0
at (wrapper dynamic-method) KSP.OAB.ObjectAssemblyPartTracker.DMD<KSP.OAB.ObjectAssemblyPartTracker::OnPartPrefabLoaded>(KSP.OAB.ObjectAssemblyPartTracker,KSP.OAB.IObjectAssemblyAvailablePart,UnityEngine.GameObject)
UnityEngine.Debug:LogError (object)
KSP.Logging.KspLog:Error (KSP.Logging.LogFilter,object)
KSP.Logging.GlobalLog:Error (KSP.Logging.LogFilter,object)
KSP.OAB.OABLog:Error (object)
(wrapper dynamic-method) KSP.OAB.ObjectAssemblyPartTracker:DMD<KSP.OAB.ObjectAssemblyPartTracker::OnPartPrefabLoaded> (KSP.OAB.ObjectAssemblyPartTracker,KSP.OAB.IObjectAssemblyAvailablePart,UnityEngine.GameObject)
KSP.OAB.ObjectAssemblyPartTracker/<>c__DisplayClass57_0:<LoadPartsAtInterval>b__0 (UnityEngine.GameObject)
KSP.Assets.AssetProvider/<>c__DisplayClass12_0`1<UnityEngine.GameObject>:<Load>b__1 (UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle`1<UnityEngine.GameObject>)
DelegateList`1<UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle`1<UnityEngine.GameObject>>:Invoke (UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle`1<UnityEngine.GameObject>)
UnityEngine.AsyncOperation:InvokeCompletionEvent ()
did you set up
add tata modules?
you did
AH
its actually something
that you helped me with
5 months ago
xD
(i thjink)
just add a null check
_dataTest ??= new Data_Test();
before the tryaddunique
Alright lets see this in the OAB
And then I'll try in flight
Wow
alright
I guess I need a name for that??
dont use module Action
its a bit more setup than you want
and you can onyl test in flight
No, thats not the error
Apparently, I have to wrap the function in a new Action(...) call maybe
I have to go to bed soon ... I have an early dentist appointment tomorrow
IT WORKS IN THE OAB AT LEAST!
I added a completely custom module to a stock part in the OAB
@north mist
AND IN FLIGHT??
it should
atomatically work when goes
to flight
cuz its a direct transfer from oab to flight
try f5-f9
And when I load a previous save with a vessel
let's gooo
Though that latter one, i have one thing I need to test
Alright tested with complete reload
Champagne was warranted
The only problem with a vessel already in flight, is that it uses the default data
Which ... makes sense
Where is the save file?
LocalLow
then
we have a problem
it saves the current state of the vessel
with all modules
independent of when it was added
its pretty late here already
Let me do one final test
Its in the saved workspace file
on the OAB too then i'd imagine
its lost somewhere
i'll be here tomorow

like we say in portugal
Amanhã há mais
tomorow there's more
{
"Name": "Data_Test",
"ModuleType": "TestModule.Modules.Module_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"DataType": "TestModule.PartDataModules.Data_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Data": null,
"DataObject": {
"$type": "TestModule.PartDataModules.Data_Test, TestModule",
"ModuleType": "TestModule.Modules.Module_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"DataType": "TestModule.PartDataModules.Data_Test, TestModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"IsActiveInStagingProp": {
"ContextKey": null,
"storedValue": false
}
}
},
Its not serializing the data though ... hmm?
is the Data class serializable?
it serializes everything in the class
its a direct serialize from JSON
as long as its pulbic it will serialize
[Serializable]
public class Data_Test : ModuleData
{
public override Type ModuleType => typeof(Module_Test);
[KSPDefinition]
public string TestData = "DEFAULT";
}
Here it s
And it put in the save file as well for the save I made
workspace uses a diffrent serialization
I made a save from the OAB and it worked
Anyways, I misnamed the commit, I meant to put module instead of part
I'm tired
I'm going to bed
OH MY GOD, THE ERRORS SUCK JUST BECAUSE I FORGOT TO INTERPOLATE A STRING
There we go, it shows the missing semicolon in the logs
:parts {
$name: $current["partName"];
+Module_Test {
+Data_Test {
TestData: $name;
}
}
}
Btw, this is what I was using to test
Could also be like
:parts {
$name: $$partName;
+Module_Test {
+Data_Test {
TestData: $name;
}
}
}
I think we can possibly do a prerelease quite soon @north mist
Main thing I wanna do is get rid of a lot of unnecessary logging
hell yeah
Also, I got the patch amount display to go up automatically as stuff is patched
So it always looks like its doing stuff if stuff is being patched
One slight error, deleting a part from the cache makes it so no parts get loaded
We can ignore this for now I hope
huh
Weird addressables stuff, I'll just warn people not to delete parts
Wait im an idiot
This is my fault, I was hand deleting them and forgot to delete them from the inventory.json
Let me try doing this correctly
Bye bye antennae
Yeah that was me being an idiot
Quick question: is the list of parts categories / families defined in json files? If so would it be in the scope of PM to be able to add new ones?
Have to check on that one, if so yes
I'm pretty sure it isn't
I was going through that stuff recently to put it on the wiki
and the only way I was able to get a list (possibly non-exhaustive) was to go through all parts and take the values from their definition files
The best way to go about doing this would probably be making a separate mod similar to KSP1's Community Category Kit: https://github.com/UmbraSpaceIndustries/CommunityCategoryKit/wiki
Which could then use PM patches to add more categories
The main issue
Part categories ... are an enum
So we can't PM patch them in, we'd have to preload patch them
Yeah, I wasn't really thinking about the implementation details anyway, mostly I just wanted to say that this is out of the scope of PM, and a separate library mod should be made for it
There ... is a way to go about this though
Preload patcher rewrite enum
I'm gonna quckly start on a test mod for this
I don't really love the idea of doing that though, since all mods that would want to make use of it would have to use reflection
Right?
Well, given that part categories for a part are defined on the JSON as a string that gets deserialized into the enum, most use cases they wouldn't have to
Or rather, not just make use of it, but any mods that would want to access the values of the enum
From code
But to do anything different would require such a rewrite of everything that its not worth it
I mean they could just use the string value of the enum
.ToString()
Once modding support is more done, I'm basically gonna suggest that it shouldn't be done like that at all
Yeah, I don't know what they were thinking in the first place
This could have easily been read from a text asset
I'd make a suggestion on the forums ... but discussing the code is not allowed is it?
I'm not sure, but I don't think so?
Since Shadow is suggesting how they should improve their code, and I even specified classes in which they use specific attributes
So I'd say it's fine
I'll write that one later
Are families stored in Enums too or are those string fields?

