#Patch Manager
1 messages · Page 6 of 1
yeah i can make an update
👍
maybe i can even do it rn
in the car to school
when i get to class
make sure to regenerate the grammar and everything
Also @frail star I added a launch step in the package.json for making the json grammar from the yaml
sounds good
in class
ill do it
did you fix the typo that was causing the antlr issue?
Yeah
Go with 0.6 I suppose
👍
publishing on hotspot wifii hits different 
published!
i wonder if it shows up in the search
💀startup time
thats not happening
if you like the content i provide, please feel free to SMASH that star button
write a comment down below
but how do I subscribe
not true
ive been writing in only zig for the past couple months
i havent ran into a project i couldnt do with it
Just because you can doesn't mean you should is my answer
Zig needs to be compiled and run on the end users computer
https://ziglang.org is written in zig
typescript here can easily be just shipped
what????
binaries:::
Yuck
wdym yuck 💀
I said what I said
Cheese would never use a binary, ew, she compiles her Discord client and browser on her own machine 
discord canary is ok
Discord is written in javascript?
But I'm saying that binaries are eww to ship in an extension
electron 
oh no, 100x faster performance on the user side and no change to the install experience
how terrible
idk, just like I feel like it's extremely stupid that nowadays everything including servers and desktop apps is written in JS/TS, this feels like the same thing
use the technology best suited for the specific use case
yeah but I'm just commenting about this
is possible =/= is a great idea
no need to get all "lol bro" about it, I just said my opinion
apologies, "lol bro" might have been no the correct wording 
criticising someone who wrote a language, and then made his langs website in that language as something "extremely stupid" is, stupid
its clearly a demonstration on the power of the language
I never said that
I said using JS/TS for everything and anything is stupid
and so is the idea of doing so in zig
....
not that the website itself is stupid
a proof of concept is cool
i never said, that you said, that the website is stupid
using it for everything just because it's possible is impractical
nobodies using it for everything 💀
a lot of backends are written in c, or cpp
lol
the rust lsp backend is written in rust
the zig lsp backend is written in zig
the backend is supposed to be very fast, not conform to a language
this is going absolutely nowhere
typescript is simply the example they provide you with
you can write it with anything
my whole point was just that not everything has to be done in zig
can we move on now?
nobody said that 
sure
@upbeat hare gonna add workflows now 👍 have a minute free
workflows running for other project will take a while

truly
Wtf are you doing sinon
i’m cooking
damn
it didn’t stay a gif
discord didn’t like me copying that gif 💀
What's the proper way of selecting parts that have a specific module? I'm having difficulty understanding the selector syntax/logic.
I found that this successfully does the selection:
.Module_DataTransmitter
Does it look for any parameter that has a value of Module_DataTransmitter? If so, why doesn't Data_Transmitter also work? And why does it need to have a dot prefix? And why isn't it PartComponentModule_DataTransmitter since that's what the value of Name is in the json file?
Also, it feels as this isn't optimal. If I know the direct hierarchy I feel I should be able to do something like this:
* > serializedPartModules > $$Name: "PartComponentModule_DataTransmitter"
.Module_DataTransmitter is correct
.Data_Transmitter doesn't work cuz it only searches for {X} or PartComponent{X}
The . is necessary because that turns it into a class selector rather than an element selector to use css terms, as in it has the class Module_DataTransmitter because it has a module with the name PartComponentModule_DataTransmitter
.PartComponentModule_DataTransmitter should also work, if not, that is a bug
Its not the direct hierarchy, as the adaptor for parts specifically treats modules as if they arent under that serializedPartModules
Or to put it otherwise, without the . you are selecting the modules themselves rather than parts with the module
Actually, hmm on that latter point I have to recheck the code, i dont recall if I made element selectors work like that
Cuz it makes more sense to usr a child selector there
When you say 'class selector', you mean literally a part that has a c# class called Module_DataTransmitter ?
I tried .PartComponentModule_DataTransmitter, doesn't work
This is my test patch:
:parts {
.PartComponentModule_DataTransmitter {
+Module_OrbitalSurvey {
+Data_OrbitalSurvey {
+MyData {
MyString: "my test string";
MyInt: 5;
MyBool: true;
}
MyStateString: "my state string test";
MyDefinitionString: "my definition string test";
}
}
}
}
It does work with .Module_DataTransmitter
Alright, thats a bug
Not exactly
Classes in this language are essentially the names of the fields or children the object has
Alright
Next, I'm trying to select only parts with a specific transmitter range. I have this so far, but it doesn't work.
:parts {
.Module_DataTransmitter {
@if $$CommunicationRange > 201000000 {
+Module_OrbitalSurvey {
+Data_OrbitalSurvey {
//+MyData {
// MyString: "my test string";
// MyInt: 5;
// MyBool: true;
//}
MyStateString: "my state string test";
MyDefinitionString: "my definition string test";
}
}
}
}
}
Ahhh this is going to be a fun one
Hmmm, actually, this will be annoying, as you are still in the scope of the part...
Yes
so I need to select the module itself
You have to do something like $$serializedPartModules[IndexOf($$serializedPartModules, "PartComponentModule_DataTransmitter"]["CommunicationRange"] to get that value ... where indexof is a function you have to create that finds the index that has the datatransmitter...
I might have to make that easier
uhh, not for the faint of heart that is...
Yeah, I definitely need to make that easier
The best way would be to add a condition to the class selector but I have to think of syntax for that
And I'd likely want a way of defining variables in the scope from that as well
Hmm actually no thats a better idea
Being able to extract values from the class somehow
.Module_DataTransmitter > (@if $$CommunicationRange > 201000000)
.Module_DataTransmitter (@if $$CommunicationRange > 201000000)
.Module_DataTransmitter (@if this.$$CommunicationRange > 201000000)
.Module_DataTransmitter::(@if $$CommunicationRange > 201000000)
Just typing something that would make sense in my head
.Module_DataTransmitter :: {
@return $$CommunicationRange > 201000000;
}
Allows you to do more complex calculations in there and such
And then, I will have all variables defined in that calculation be copied into the current scope
So maybe I just do that and you can have an @if in the actual block
sounds good
So it would end up something like
:parts {
.Module_DataTransmitter :: {
$commsRange: $$CommunicationRange;
} {
@if $commsRange > $MIN_RANGE {
// ...
}
}
}
Yep, that's perfectly understandable
I'll work on that once im home and maybe it can be in a release soon enough
and this is why MM's syntax needed to do so much lol
oh yes
This specifically shan't be too bad though
I understand specifically why this is necessary too
As the alternative syntax is ugly
Ooo, this is going to be fun to add to the language
not that I have any better idea right now, but I kinda hate the { ... } { ... } syntax
🙈
I'd rather have any executing "code" kept in curly braces
I agree, but then I'd at least add another separator between the braces or something
this looks kinda confusing
I'm not sure what I would add
.X :: {}! {} -- too ugly
.X :: {}; {} -- maybe?
.X :: {}: {} -- too many colons
.X :: {} :: {} -- Same as above, and looks almost like 2 capture blocks
:parts {
.Module_DataTransmitter :: {
$commsRange: $$CommunicationRange;
};
{
@if $commsRange > $MIN_RANGE {
// ...
}
}
}
That would be with a semicolon
I mean it does make sense when you consider the ::{$commsRange: $$CommunicationRange;} part a pseudoelement
though maybe then a single colon would make more sense, being a pseudoclass?
I still kinda think regular parentheses would look better, since curly braces are meant to open and close the scope of the preceding selector
and the $commsRange: $$CommunicationRange; here is actually part of the selector
I disagree as there is no other case where you can assign variables and do arbitrary code inside parentheses
or, square brackets like when using captures in C++ lambas?
I guess that may work
The parser treats the inside of the stuff like a function body but, yeah that should be fine
:parts {
.Module_DataTransmitter:[$commsRange: $$CommunicationRange] {
@if $commsRange > $MIN_RANGE {
// ...
}
}
}
I do think that has much better readability
I'd rather keep the semicolon, it makes parsing a lot easier in the case of multiple captures
it would be ideal if you could make it optional for a single statement, like when using inline styles in HTML, like
<div style="display: flex">...</div>
but if that makes it more difficult, then it's fine
Also, community resources likely is going to have to be rebuilt after this update
:parts {
.Module_DataTransmitter:[$commsRange: $$CommunicationRange;] {
@if $commsRange > $MIN_RANGE {
// ...
}
}
}
Will be what it ends up like
though I prefer the following formatting
:parts {
.Module_DataTransmitter:[
$commsRange: $$CommunicationRange;
] {
@if $commsRange > $MIN_RANGE {
// ...
}
}
}
that looks good 👍
Now time to test it
@use 'builtin:debug';
:parts .Module_DataTransmitter:[$commsRange: $$CommunicationRange;] {
$test: debug-log(["Communications Range: ", $commsRange]);
}
That is going to be my test "program"
Or better yet
@use 'builtin:debug';
:parts .Module_DataTransmitter:[$commsRange: $$CommunicationRange;] {
$test: debug-log(["Name", $$partName, "Communications Range: ", $commsRange]);
}
I mean it somewhat works
Ahh I'm just an idiot
I made a dictionary static that shouldn't be
@desert stirrup here is that system working, which will likely be in the next version of Patch Manager
@north mist can you upload this nuget package so I can make sure to rebuild community resources before I release PM 0.4.0?
Actually let me check if I need to first
btw you should also be able to push packages, since you have the API key
Oh yeah
dotnet nuget push file.nupkg -s https://nuget.spacewarp.org/v3/index.json -k API_KEY
Ahhh, thank you
anyways, I made a PR as well for patch manager (ignore the fact that its 37 changed files, I had to change a lot of the stuff around selections)
I made env vars for the server name and API key, so that I can just write dotnet nuget push file.nupkg -s %SwNuget% %SwApiKey%, and for some stuff like the UnityEngine.Modules and KerbalSpaceProgram2.GameLibs I even have bat files with that so that I can just double click to upload the newest package 😆
I already went through the commit when you made it and damn
that was a lot of changes indeed

Could you put an approval on the PR then?
yeah lmao sorry, forgot
nice, thanks
oh it's already released, very nice
Also @frail star is going to have to update the grammar file for the vscode extension
So you are using this mod as a tool for your orbital survey mod, makes sense
Yes, attaching a custom orbital survey module to all antennas, but I think I'll want some control for which antennas will be eligible, to add a gameplay feature for attaching larger antennas. I'm still playing with the concept, nothing set in stone.
It works 🙂
Awesome!
I wonder if I can have patch manager run on multiple threads
Ohh you'll have to enter the wonderful world of parallel scheduling of task dependency graph
those words sound scary
Don't have edit rights for Patch Manager wiki, so I'm sending you this if you want to add it to https://github.com/KSP2Community/PatchManager/wiki/Reference#collection-of-patch-case-studies
I should give you perms to edit it
sure
I think we need to advertise the existence of PM more, most people outside of a small circle of modders here have no idea it exists
Ohh you just reminded me I completely forgot to write the forum post
@north mist how does this look for a forum post
looking good, though, being a bit of a grammar nazi, I'd fix the "it's purpose" to "its purpose" in the description
sure
Patch Manager A mod for all your generic patching needs! Spacedock: https://spacedock.info/mod/3482/Patch Manager Github: https://github.com/KSP2Community/PatchManager Author: KSP2 Community Description Patch Manager is a mod for KSP2 that functions as a sort of spiritual successor to Module Mana...
:parts (#eva_kerbal,#seat_0v_external_crew) > resourceContainers {
// ...
Well, I'm glad me adding parentheses into selectors actually was useful
(from safartes eva patch)
I want to see about adding implied stages for mods, and then the custom stages as well
where the implied stages are like (position in load order * 100) for the priority
and then I kind of want a custom stage definition that can be like modImpliedStage + N, plus a global priority kind of one
it will definitely become useful when we start getting deeper into mods patching mods patching stock stuff etc.
I suppose I should start work on something like then
I might go for a granularity of like *10000 and then actually multiply custom stages by 10 so that there is always enough gap for a @before ... @after ...
but hmm that means i'm going to have to add all the information into SCSS to know where a library came from and therefore what its implied priority is
And then, I'll have to also know how many mods are installed to make an actual 0 global priority that is after every mods patches priorities
so basically
@local-stage "xyz", 9; // Creates a stage with a +90 offset from the implied priority
@stage "xyz", 9; // Creates a stage with a +90 offset after all implied stages, a +90 global offset
And when importing libraries, the stages from other mods get prefixed with the mod id like usual
so like if you imported that, the stage name for xyz then becomes mod_id:xyz
This is probably a complex system, but it likely will be necessary for the ability for people to perfectly time their patches with other mods
yeah, it will definitely be more complex than the stages in MM, but then that was one of the biggest pain points of MM, so this might solve most of the issues
Yeahh
One note all the implied stages can also just be referenced by their mod id as well
Does PM have a ternary conditional operator?
Oh nice, I'll use it in my KLSS EVA patch then x) (EVA Kerbals need 0 starting resources but external seats need 1 day)
x @if y @else z (idk jf i removed the ats or not
@stiff crater correction
x if y else z
I'm also going to predefine modid:post as modid+9995 or something like that
so to do after a mod
@after 'modid:post'
...
And to do before a mod
@before 'modid'
...
And I'm going to make @define-stage default to local
With @define-global-stage being for global stages
Anyways @north mist pushed that thought process to dev
lemme actually fix one thing
There we go
so
@define-stage "stage", 10; // defines a local stage to this mod with a priority of 10
@define-global-stage "global_stage", 100; // defines a global stage with a priority of 100 (still prefixed with modid if used elsewhere
Also, you should really only be defining your stages in one patch file (stages.patch) or whatever, as long as they are defined consistently throughout the mod, they will resolve the same
and that file should not be a library
the stage names are global and are computed before anything else is run
But not from libraries
Thoughts?
yeah I think that's fine
What's a local and what's a global stage? First I understood it as: global stage defines when this patch is run in relation to other patches of other mods and local stage defines locally within the mod when the patch will be run in relation to other patches of the same mod.
But then you said "you should really only be defining your stages in one patch file", so what I thought was wrong
Okay lets define it a bit easier based off of this convo:
So, how I just made it work, each mod gets its own subblock of priorities (10000 separate ones per mod) dependent on their location in the load order, this is the mods implied stage, and has the id of mod_id, the other autogenerated stage here is mod_id:post which has the priority of mod_id + 9995. This implied stage is also the stage that attributeless patches get.
A local stage then is a stage who's priority is relative to that of the implied stage, so a local stage with an in text priority N will have a global priority of mod_id+N*10 (*10 to allow for the @after and @before attributes to always generate a priority in between stages if necessary), the priority of these is 0-999 in the definition, but one mods local stage with a priority 5 will not always run before a mods stage with a priority 10, it is dependent on the load order of the mods.
A global stage is a stage such that the zero priority is after all mods implied priority blocks, but its priority is absolute, a global stage with priority 5 will always run before a global stage with priority 10, and always after any local stage of any mod.
The reason I suggest to define them all for a patch with one file, is such that if you keep redefining them across files, it gets harder to keep the definitions consistent.
The @after/@before attributes mentioned before do the following, @after 'stage' means that this patch will always run after a stage if said stage exists (otherwise default to the @stage attribute or the mods implied stage), @before 'stage' does the same but as the name implies before. These attributes can then be combined
Or lets say we have three mods, a, b, and c for the sake of this
a
@define-stage "test", 5; // Local
@define-stage "test2", 3; // Local
@define-global-stage "test3", 10; // Global
b
@define-stage "test", 3; // Local
@define-stage "test2", 4; // Local
@define-global-stage "test3", 32; // Global
c
@define-stage "test", 1; // Local
@define-global-stage "test2", 1; // Global
Goes to this priority map
|
V
----------------
00000 - a
00030 - a:test2
00050 - a:test
09995 - a:post
---------------
10000 - b
10030 - b:test
10040 - b:test2
19995 - b:post
---------------
20000 - c
20010 - c:test
29995 - c:post
--------------
(30000) - the implied priority base for global stages
30010 - c:test2
30100 - a:test3
30320 - b:test3
I hope that helps convey what I mean by those terms and how they interact
It's clearer now, thanks. Though I'll need to re-read it a few more times when I'll have better concentration (dealing with kids now)
a:test2 should go before a:test, right?
yes, it should, I misread what I wrote when doing the mental math
fixed
Ill likely transcribe what I wrote here to the wiki at some point
Ok, I reread it and I understand it. Nice system! More complex than MM, but that's needed in the long run.
Couple of questions.
Go for it
so if in mod B you write @after 'A' and mod A has other stages, what will be the priority for mod B patch then? After all the stages of mod A or just after all patches of mod A that don't have a stage?
Thats why A:post is autogenerated
You want to do @after "A:post"
And all patches that dont have a stage implicitly have stage A
so @after 'A' would have 00001 and @after 'A:post' would have 09996 ?
Yes, kind of, I do a quirky average thing rn, but I should just do a + or - 1
I dont see why I do that
Its more so @after 'A' >= 00001, @after 'A:post' >= 09996, and all other being equal, @after 'A:post' > @after 'A'
But yeah ... ill just switch it to a give or take one system, that should be fine for now
So if I understand correctly, there's "space" for 9 "after" priorities for each stage? So if a patch is 00000 there can be 9 more patches from other mods that target after 00000 (00001-00009)? Maybe that's enough, I don't know. But I've seen that RSS/RO patches can be really really long with lots of "afters"
Well, the amount of afters doesnt really matter as itll go after the one with the greatest priority
The reason for that space is if i start making dynamically generated stages defined based on afters/befores
And if I need to, increasing the space is easy enough
Same with increasing the block size per mod, both of these are implementation details that shouldnt exactly change the behaviour of any patches
And im working with ulongs, so I have plenty of room
Don't know if I understand it fully... to expand on the example: mod A has 00000, then mod B patches after A and it's 00001, then mod C after mod B so it's 00002... then skip to the end we have mod J that patches after mod I and mod J is then 00009. Can there be a mod K that patches after J? It would be 00010 then? or 000091? It's a really complex example, I don't think it'll come to that, but for the sake of understandment... 🙂
Well mod B as a whole doesnt patch after A, its individual patches do
I get that, in this example mod B wants to do an @after for mod A
Then mod C cant patch after that under the current system but thats what i meant by dynamically generated stages in my prior message
As that patch doesnt have a stage name
But thats a complex thing I have to find the math for doing
So essentially that in itself is not possible yet
theoretically, does this system even need any numbers at all? couldn't the patch priorities be stored in some sort of a priority graph?
Honestly ... maybe, its just that when we designed pm, we had other syntaxes in mind, hence the entire priority sytsm
But I suppose I could do all the graph transformations in the engine and translate it to a priority system
it would save you the trouble of doing all this math and then possibly having to do resizing when someone runs into issues
Why did I not think of this
Okay
I'm overcomplicating things
Then I have to define all stages as relations to eachother
Wont I?
yeah I think that should be enough
As stages are just named graph nodes
the issue with MM was that there were only predefined ones
stuff like :BEFORE, :AFTER, and :FINAL
Ahhh yeah
@desert stirrup ... I hate to say it but all of what I described may become a teensey weensey bit outdated
that's perfectly fine, as long as we're improving on things 🙂
basically we need to be able to do things like say "mod A's patch runs before mod B's patch, and mod C needs its patch to run before A's patch of B"
and mod C needs its patch to run before B, but after A
And thats where named stages will be
@define-stage "AofB" {
@before "B";
}
Itll be more complex syntactically as its just a bunch of relations
also, MM had a syntax to trigger a patch if a mod exists. I didn't notice that on the wiki reference, but that would be trivial I guess
@require/@require-not
ah yes, missed it
can those be composed into more complex relations?
like "require mod A but only if B and C are not installed"
I'm realistically trying to think of the various patches I remember seeing for example for RP-1
Maybe I could make @require do boolean logic
since that's probably the most complex use case of MM
if we can make RP-1-like patches work, then everything else will probably be fine
lol
Id literally have to introduce boolean logic for that
@require "(B || C) || (!(B || C) && A)"
Is what you just told me
Wait
or, in the spirit of SCSS, more like
@require 'modA' and not ('modB' or 'modC');
or keywords if Patch Manager is more targeted for non-programmers (B or C) or (not(B or C) and A)
sure
I guess it doesn't matter too much if we're using Sass or SCSS syntax
it's very similar, other than brackets and semicolons
(for reference)
The brackets stuff is easier to parse
time to get crackalackalackin on some code
@desert stirrup @stiff crater do either of you use the ! for not anywhere in your code, as that is likely going to be changing to not after this change to be more consistent with and and or?
Or I suppose I could do both nvm
I'm not using it
I would probably go with just 'not', while there aren't too many patches using '!' yet
it'll be less confusing to only allow one syntax
yeah, I agree
so what's the negative of @if $commRange > $minCommRange ?
@if $commRange not > $minCommRange ?
(I suppose it could be written as <= but just for the sake of argument
@if not ($commRange > $minCommRange) ?
yeah the 2nd one definitely
not > is harder to parse than not (...);
you wouldn't write ($commRange ! > $minCommRange)
Well not too much harder, but a bit uglier
right
Anyways, working on the syntax for require expressions
I'm just thinking as a non-programmer. if this value is not larger than this other value
I think we shouldn't go completely like that, <= is easy enough for a non programmer to understand
boolean logic is a bit more ugly and language dependent
alright
(my own programming language uses words for it for that exact reason)
Like using && for boolean and is entirely a computerism, while <= is taught in schools
well if you want stuff taught in schools, you could do @require 'modA' ∧ ¬('modB' ∨ 'modC');

Okay, but not everyone has a boolean logic class either
I may have had such a class but my degree requires it
eh I mean this is like high school junior year level stuff
true 
Honestly, with this I can get rid of @require-not
@require-not "mod"
; and
@require not "mod"
Mean the exact same thing
yeah, I was thinking that as well
Okay new stage definitions
@define-stage "XYZ": @implicit; // Defines this stage as a stage that is implicitly after the previous implicit stage definition in the current mod (within file order, between files, order is undefined)
@define-stage "XYZ": @implicit-global; // Defines this stage as a stage that is implicitly after the previous implicit global stage definition
@define-stage "XYZ": { // Define a stage based off of its relationships precisely
@after "ABC"
@before "WYZ"
};
@desert stirrup @north mist what do you think?
Maybe I'll get rid of the implicit from @implicit-global
I was thinking that maybe you could get rid of @implicit (since not having any relationships defined kinda implies the same thing), and call global just @global?
yeah thats likely better
So now
@define-stage "XYZ"; // Defines this stage as a stage that is implicitly after the previous implicit stage definition in the current mod (within file order, between files, order is undefined)
@define-stage "XYZ": @global; // Defines this stage as a stage that is implicitly after the previous implicit global stage definition
@define-stage "XYZ": { // Define a stage based off of its relationships precisely
@after "ABC"
@before "WYZ"
};
?
yeah I think that looks cleaner
looks good
And then I'll have the post stages for each mod to be defined as after the last implicit definition within this mod
so basically you have a mod mod that defines
@define-stage "A";
@define-stage "B";
@define-stage "C";
Then mod:post will basically be
@define-stage "mod:post": {
@after "mod:C";
};
implicitly
This is going to be a fun topographical sort
Honestly I kind of want to remove the
@after ...
@before ...
From patch definitons
Reason being, that I want every single stage to be explicitly defined
So if you want to do
@after "XYZ"
:parts ... {
}
You then have to do
@define-stage "after-xyz": {@after "XYZ"};
@stage "after-xyz"
:parts ... {
}
I know that that is a lot, but it forces people to write their stages to be interceptable
Thoughts on this?
yeah I do agree, we want patches to be as "extensible" by other patches as possible
yeah, this change alone makes a huge improvement over MM
You'd need to define the stage only if you're targeting another stage or always?
ideally always, so that another patch can target any of your patch stages
yeah, was thinking that aswell
The implicit stage name of any patch is mod_id, but you likely want to make that more granular
yeah, that makes sense, if a patch only does one thing, you only need the mod name to target it
otherwise, you need to target a specific stage name
👍
and if mod A and mod B both target the same stage from mod C, what determines the execution order?
undefined, unless they explicitly state an execution order by having an @after on the other (which will not break if the other mod does not exist, it'll just be ignored)
Mod A
@define-stage "pre-c": {
@before "c"
};
Mod B
@define-stage "pre-c" {
@after "a:pre-c"
@before "c"
}
Mod C is whatever
I'm thinking what can be done if mod A and mod B don't know each other and don't write anything about each other... but I guess you can't account for that
and if a:pre-c does not exist, then this constraint is ignored
I don't have magic insight into the minds of patch writers unfortunately
if they mess with the same area in a possibly incompatible way, then they have to have explicitly defined compatibility
Would alphabetical execution be preferable to a random execution?
though ... I am thinking of adding @require expressions to stage definitions so the stages can be changed depending on what mods there are as well
I don't like the idea of it because then it invites the disgusting stuff like "zzzModName" that KSP1 mods were doing
I know. But it's inevitable that patches will be written that will target the same thing, without addressing other mods, so I'm thinking what could be done to await the Z-wars... nothing I guess
The reason for this, is you may want more granularity of where a stage runs depending on loaded mods, and you don't want to have to do multiple stages and duplicate patches
by forcing mod authors to write compatible patches if they do something on the same thing
if they target the same thing, and it breaks, thats on them
if we add a secondary way to define the resolution order, then they will abuse it
otherwise, one of them may get tired of the complaints and decide to make their patches compatible
yeah, I would leave the order of patches undefined on the same-stage level
yeah that makes sense
It would be similar to the require expressions for patches
@require not "xyz" and not "abc"
@define-stage "something";
@require "xyz" and not "abc"
@define-stage "something": @global;
@require "abc"
@define-stage "something": {
@after "abc"
};
A bit of a weird example, but the point is made I think
Does that look odd munix?
I'm wondering if we could wrap all this boolean logic in a somewhat more consistent way for all this top-level stuff
like using if-else statements or something like that
so that we don't keep introducing new syntax for everything
It's the same syntax copied from patches
but, also, top level conditionals exist as well
so there is a way to do
@if condition {
} @else {
}
But how im doing stages right now is kind of a preprocessing pass, but hmm, I suppose I could have a builtin function called is-loaded() as well
or @if-requires also works?
@if-loaded "xyz" and not "abc" {
}
@if-loaded not "abc" {
}
That kind of works, yeah
I was kinda thinking something like
@if exists-stage("modA") and not exists-stage("modB") {
@define-stage "myStage": {
@after "modA"
}
}
or, with slightly different semantics:
@define-stage "myStage": {
@after "modA" @if exists-stage("modA") and not exists-stage("modB")
}
though it is more verbose with the separate exists-stage function
I mean, exists stage is different than the require semantics I was thinking
But I could allow that, that would go into a meta library
like basically my idea was that instead of a top-level @requires statement which applies to everything below it, a scoped @if with everything in its scope applying if the condition is met
@use 'builtin:meta'
@if exists-stage('modA') and not exists-mod("modB") {
@define-stage "myStage": {
@after "modA"
}
}
and then
@if-loaded and @else-if-loaded as special cases for doing operations purely on exists-mod?
exists-stage is going to be hard, as if you can define stages based on the existence of stages, and the order of patch reading is assumed to be random, how would you know what stages exist?
so something like
@require 'modA' and not ('modB' or 'modC');
.parts {...}
could become
@if exists-stage('modA') and not (exists-stage('modB') or exists-stage('modC')) {
.parts {...}
}
I mean, what would the difference be between that and just @require?
Fair point
semantically they seem like the same thing, basically just with or without brackets
ah
I can't have stage definitions be dependent on the existence of other stages, or else that is going to be a nightmare
yeah, I can imagine, well, then disregard exists-stage, and just replace that with exists-mod in my examples
I kind of don't want stages to be under a conditional at all, except under mod guid, as I want to get stages in a first pass
but that doesn't matter in any case
if someone uses exists-stage in the top level of their mod, then it is undefined behaviour, if they use exists-mod then it is defined
(I say at the top level, as by the time patches are run, stages are known)
the conditional syntax is slightly more verbose, but it also allows you to change this:
@require not "xyz" and not "abc"
@define-stage "something";
@require "xyz" and not "abc"
@define-stage "something": @global;
@require "abc"
@define-stage "something": {
@after "abc"
};
to the (imo) more readable:
@if not exists-mod("xyz") and not exists-mod("abc") {
@define-stage "something";
} @else if exists-mod("xyz") and not exists-mod("abc") {
@define-stage "something": @global;
} @else if exists-mod("abc") {
@define-stage "something": {
@after "abc"
};
}
or, do something like:
@define-stage "something": {
@after "abc" @if exists-mod("abc")
};
to only add the "@after" if the mod it targets exists
or better yet
@if exists-mod("abc") {
@define-stage "something";
} @else-if exists-mod("xyz") {
@define-stage "something": @global;
} @else {
@define-stage "something": {
@after "abc"
};
}
(more readable)
yeah
I'm not going to do this one tbh, it feels a bit odd to me (also that does literally nothing that wouldn't be the default behaviour for that example lol
yeah, sure, it's only a shorthand anyway
and yeah it does not look like something that would have a lot of uses
[SassyLibrary("builtin","meta")]
[PublicAPI]
public class MetaBuiltins
{
[SassyMethod("exists-mod")]
public static bool ExistsMod(GlobalEnvironment globalEnvironment, string modName)
{
return globalEnvironment.Universe.AllMods.Contains(modName);
}
}
Bada bing, bada boom
Gods I love that I wrote a whole C# interop library early on
awesome
Running into the issue where I'm not getting any log info out of the sassy patching stuff ... again
Like take this
THIS ISN'T LOGGING ANYTHING
wtf
And it literally has to run
what if you just try Debug.Log?
What the fuck happened to my logging
Oh and it still works perfectly fine in CoreModule.cs
It's not like its 2 different fucking assemblies either (which shouldn't matter)
Okay, time to clear out everything
bro

I'm committing murder
I'm committing murder now
What the fuck
What the fuck
Ohh
God damn it m unix
@north mist can this be moved to OnPreInitialized somehow?
I mean
you can try
I'm not sure if it needs to be in Start or not
I think it was there because of possible custom modules from other mods?
Why would that matter?
actually no that makes no sense
I guess you can just move it
if something breaks, we'll notice it anyway lol
That one works
I think the issue though is that loading actions need to be added in Awake/Start()
Which means I need to know if the cache is invalid before I register patches
Cuz I no longer see PM actually doing anything
ah yeah
OnPreInitialized happens already in a flow action
what was the change that requires the initialization to be moved, anyway?
Its the fact that the PluginList is only set after the Awake() method of the GameManager in KSP2 is called
I need to hook preinitialize
And there is nothing to do so in the current structure
I'm changing some of the module API
Finally, for the sake of all that is holy
Yes, every single mod is getting a stage, it doesn't really matter, as stages aren't technically "run" per se, it'll add no execution time when running the patches
This next update is gonna be a doozy, sorry for when you have to review this munix
I hate C#, I hate unity, I hate KSP2, I hate computers <- my brain rn
Gods writing the documention/tutorials for this is going to be fun
I already have wiki reorganization on my mind (for the general KSP2 modding stuff)
yeaaah the docs are really the weakest point of the whole modding community right now
like 90% of all knowhow is just "go ask someone in the Discord"
lmao
But like its an overwhelming amount of stuff to write on top of everything else
Especially when we do this in our freetime and suchhh
I guess I should do my part and at least write the docs for UITK and LFO
https://github.com/KSP2Community/PatchManager/wiki/Tutorials
Starting work on some tutorials for PM, but will have to do more tomorrow
(am watching a movie rn)
Okay, time to write a bunch of empty tutorial blocks with [TBD] under them so I know which ones I need to write
I think the UITK docs are a good opportunity to try using Writerside for a project
Tell me how that goes
I'm genuinely interested
Screw it i might try using writerside as well it may help me organize better
Oh I really like this
yeaah I love the tabs
Not me recommending people to install VSCode for Patch Manager
How hard would converting the VS Code extension into a Rider plugin be? 🤔
I know nothing about rider plugins
I think I'm going a bit too all out here
(the latter three are going to refer to other documentation)
I was wondering if this isn't a bit out of scope lmao
It's not entirely out of scope, its mostly to get people to structure their mods correctly, and people coming into spacewarp may not know what a codeless mod is anyways
I'm just not going to 100% explain how to setup unity/spacewarp templates, and refer to the correct places for that
Though I'm writing this for the perspective of someone who wants to pick up patch editing with maybe some small experience with KSP1 stuff and such
oh yeah, for sure, though I might have just said something like "if you only want to write patches, do xyz, otherwise, visit the wiki/SpaceWarp docs to see how to create a project"
but this is essentially the same thing
just more verbose
Yeah
really the comment was mostly meant as "at first I thought you were going to write full guides for all 4 of these on the page"
lol
oh gods no
https://github.com/antlr/jetbrains-plugin-sample seems like it should be possible to use the Antlr grammar for it at least, but it's not the officially supported way
the rest of the extension would mostly have to be completely rewritten
Just me casually linking semver.org when describing what the version field is
well the rest of the extension is a custom grammar to try to approximate the antlr grammar atm
The fact that we use an antlr grammar here might make this easier for us for intellij/rider
Like this is all I am saying about unity here
Compared to this for standalone
hm the libraries folder is in the root?
I kinda dislike that
wouldn't patches/libraries be better?
to have all PM stuff in one folder
I suppose, I mean it wouldn't be hard to subtly guide a better structure yeah
because <mod_root>/libraries kinda makes me think it should contain 3rd party DLLs that the mod loads
thank you 😆
does Patch Manager explicitly search those folders, or does it just look for all .patch files anywhere in the hierarchy?
It'll look everywhere under a mods hierarchy
I'm just trying to subtly encourage organization
ok, yeah, then I think it's definitely better to have a single common folder for all of them
oh I'm definitely with you there
it was one of the main reasons I made the mod templates
lmao
I am likely going to create some form of PM template for somewhere at some point, but like its 1 file and 2 folders
which reminds me, I could update the template to create those folders automatically for all mods
Or yeah, just make the patch folders yeah
since I already create the full hierarchy for stuff like addressables, images, etc
Funny thing now, when adding mods to ckan, anything that has a .patch file automatically gets analyzed to have a dependency on PatchManager
(I wrote the PR for that one)
I saw, that's great
And now Patch+Code is completed
Patch+Code+Parts is going to be synthesis of the two
then I suppose you could get rid of the 3rd paragraph
I intend to release the new 1.6 templates today or at latest tomorrow
And these docs wont be done before then
Also if anyone else wants to work on the docs with writerside
for now I'm just putting my UitkForKsp2 docs into the main repo docs folder, since then I can include snippets from the code files without copying them
which is not really applicable to PM, but it's a very nice feature
You know, I might want to make a jetbrains extension specifically so I can use patch files in writerside
That'll come after I finish up the first version of the docs though
oh I didn't even think of that
though, do you think just having language support will be enough for it to correctly render to HTML?
I feel like you would need to extend the Markdown converter itself
(by the way if not, SCSS syntax highlighting works pretty well in most cases)
(except WriterSide doesn't have SCSS support)
hm
in that case I guess CSS still kinda works as a fallback
though it will definitely have some issues
Well, its time to write some java

Hey, if I get this working, it could be useful eleswhere
Alright, this is a bit much for today
Nevermind, I like pain
I am doing this

Okay the setup instructions for that don't even work, nevermind
yeah that specific thing is like 5 years old...
so it's possible it's just completely outdated by now
hmmm
yeah the official way is to use a BNF grammar
Time to write a few more pages of documentation today, aaaaa
I definitely like how Writerside renders tables
Awesome, I can change the depth of the sidebar
Almost done writing the tutorial page on values, variables, and expressions
this is so damn long
I just had an interesting novel idea I think
configs, a globally accessable data structure existing entirely within PM
Essentially these would be data structures to store information or even give new information to other patching systems, they would have labels and names as well, but those would only be used internally
An example would me
@create-config "label", "name", {
something: 5
};
Where after the label and name can be any value
This could then be grabbed anywhere at patching time using the get-config and get-configs methods from builtin:config
get-configs(label) will return a map of all configs with that label
get-config(label,name) will return a specific config or null if there is none found
And this will be accessible between all patch files of any mod
Any thoughts?
would those be mutable or immutable?
So some type of constants global across all mods?
The initial plan will be immutable, then I already have ideas for mutability, itd basically be having to compute and cache the state at every stage
one scenario where I can imagine something like this being useful is the same use case as CSS variables
Basically yes, but the labels are there in case mods want other mods to be able to describe specific overrides for parts of their patches was my thinking
mod A defines some variables with default values in one stage, mod B modifies the variables, and mod A then in another stage uses them to patch something
Well the default values would exist at the beginning before the stage progression
or that, sure
And then when I add mutability, thats when the values can be changed and cached per stage
Its just that that is going to require a lot of thinking architecture wise
Its like all mutations would apply at the end of the stage
But they would all be precomputed for every stage cuz of how stages actually work
But do you understand my thought process for this?
I can definitely see use cases for this: for example a RSS-esque mod might want to replace the default day duration used by other mods in their patches
Like lets take KLSS for example, it defines a default LS resource value for all crew containers, but what if a mod wants to add its own default, without adding the resources in the prefab. Then there are 2 options, it could do a patch after KLSS or if KLSS supports it, instead do something like
@create-config "KLSS", "part_name", {
days-food: 5,
days-waste: 3,
//...
}
Where when klss is patching a part, it can see if a config has already been created for this part and act upon that
This i can see as an argument for static mutation beforehand, maybe I can make some form of config mutator thing that runs before all the stages
But that'd require some work in itself
This was my use case that I had in mind though
@stiff crater could you see yourself supporting that kind of configuration?
And maybe for the other thing you said:
Maybe this
@create-config "KLSS-config", "day-length", 86400; // just for this test
Then at some point
@update-config "KLSS-config", "day-length", [optional stage, if no stage, presumed to run before stages start], $value * 2;
But remember, you should only do get-config in patches
As thats after all the configs have been created/updated in some cases
Ill work on this more after I finish the current documentation, but I feel as if a system like this could be quite useful
Ill probably make it an error to do it too early
Yeah if this makes modding other mods easier this would definitely be cool to have
Awesome
Thats its entire point
The reason I put a label name kinda system btw, as the name is meant to in some cases be a part name, while the label the true configuration
Btw Munix, if I add this config system, I am kinda gonna want you to add another subfolder to the templates under patches, patches/configs
Just for more organization of course
sure
I'm also going to add a builtin:parts builtin library to patch manager at some point specifically for utilities to do with editing parts and modules and the like
I just thought of the best way to think of top level statements that aren't selection blocks
Like a preprocessor
kind of
Me making more suggestions for people to make their patch libraries to be interopable
Do y'all think this is a good naming scheme to prevent overlapping declaration names in the future?
Anyways, changed the links on the forum post to link to the new location for the documentation, as it's already more useful than the main documentation IMO and will be getting better
And once I finish the general syntax tutorial, and transpose the syntax reference, it will be in a doubly more featureful state
Especially once I add tutorials for part patching and the like of different types
I'm reading the docs and I have a question
$a: 10;
$b: $a-11; // Becomes -1 as 10 - 11 is -1
$key-1: "b";
$test-2: $map[$key-1]; // this results in 2, as the map at "b" is 2
in one of these, $<string>-<number> is an expression of subtraction, and in the other it's the name of a variable
Yes, actually
That is intended
I know it can look confusing, which is why I keep using variables with - in them
what if I had
$a: 10;
$a-1: 5;
$b: $a-1;
Though those docs are wrong
is b equal to 9 or 5?
there should be a space in the $b: $a-11
let me fix that and put a warning in the docs
gotta be honest, I'm not a big fan of the dashes in variable names, I feel like it will just be confusing more than useful
I directly carried it over from SCSS
I want some form of variable syntax that is decent for prefixing stuff, and honestly since i use dash in a lot of the @ symbols, using it here makes sense to me aaaaa
This is what I have now
yeah I'm not a big fan of this whole mess either tbh
I don't have space separated lists for a reason
yeah I suppose it should be fine if all the operator examples actually have spaces around them
just to make it clear
I will do that yeah
(theoretically you could also disallow the first character that comes after a dash in a variable name to be a digit)
Honestly, that'd be nice, and also disallow it to be the last character
give me one sec, I can do that
but honestly great job on the docs, I've only gotten to like 1/3 and I'm already amazed at how detailed they are
MM could never 👀
Fixed on dev branch such that those types of identifiers cause a syntax error
Or I hope they do, otherwise it would parse into $a - 11 which is also fine
So I can remove that warning from the docs
Wait, I need to remove - followed by . as well
as
$a-.5
honestly just allowing [a-zA-Z] would probably be fine, too
yeah that sounds good
Yeah, better than putting a warning there, just make the error not possible to happen
and since variables must always start with $ that can't accidentally be 2 variable subtractions
Works perfectly, and isn't confusing, as you can never create a variable with the name $a-5 to refer to as $a-5, or that would cause an error
Why do you think its been taking me multiple days to write them
Note not every page is finished or even startred on
why does module manager have space separated arrays
the syntax mostly comes from the KSP config files
e.g.
wait lmao
you can tell I'm sleep deprived because I completely missed the commas
ah but here
I knew it was in there somewhere 
Yeah, thats just ugly
and the fact that MM has separate syntax for , and separated arrays
Hmm, I have another idea
I should make @use "builtin"; include all builtin libraries
analogously, @use "modName" could include all libraries from a mod
(if you don't already have a library in your mod named modName)
Wouldn't @use "builtin:*"; be more explicit? Also what happens if there are symbol name conflicts for use statements?
Actually, thats a good idea
ah I do like that more
In the docs so itll be ready to ship soon
There its on the dev branch
Any @use "mod_id:*"; will import all libraries from said mod_id
if you wanted to be extra fancy, you could even do something like JS @use "mod_id:{lib1, lib2}"
but that might be overkill for this
Yeah no
That's a bit much
Also I don't want to do that much more parsing on the library name
@mixin scale-max-temp-by-and-mass-by($mass-scale,$temp-scale:$mass-scale) {
mass: *$mass-scale;
maxTemp: *$temp-scale;
}
Oh yeah, its possible to have mixins default values refer to the values of previous arguments
Because arguments and default arguments are evaluated left to right at inclusion time
oh that's pretty cool
The same is true for functions as well
The default arguments are all calculated at call time
There we go, mixins have been documented
https://pm.kerbal.wiki/mixins.html
Anyways, the @create-config system is going to be a great usage for functions, as mods might define functions such as
@func klss-create-part-override-config($days-food:1,$days-water:1,$days-oxygen:1) {
@return {
days-food: $days-food,
days-water: $days-water,
days-oxygen: $days-oxygen
};
}
And if anything changes and more parameters are added or removed, it won't break stuff
and then the other mod author can do something like
@include "builtin:meta";
@if exists-mod("klss") {
@include "klss:config";
@create-config "klss", "part_name", klss-create-part-override-config(10,10,10);
}
How would the result of such a function be used in a patch?
Lets copy a simplified version of one of your patches
@use 'constants';
@use 'builtin:config';
:parts ~#eva_kerbal ~#seat_0v_external_crew {
$possible-override: get-config("klss",$$partName);
$days-food: $possible-override["days-food"] @if $possible-override @else $POD_CAPICITY;
$days-water: $possible-override["days-water"] @if $possible-override @else $POD_CAPACITY;
$days-oxygen: $possible-override["days-oxygen"] @if $possible-override @else $POD_CAPACITY;
* > resourceContainers {
$crewCapacity: $parent["crewCapacity"];
@if $crewCapacity > 0 {
+Oxygen {
capacityUnits: $days-food * $OXYGEN_PER_DAY * $crewCapacity;
initialUnits: $days-food * $OXYGEN_PER_DAY * $crewCapacity;
}
+Water {
capacityUnits: $days-water * $WATER_PER_DAY * $crewCapacity;
initialUnits: $days-water * $WATER_PER_DAY * $crewCapacity;
}
+Food {
capacityUnits: $days-oxygen * $FOOD_PER_DAY * $crewCapacity;
initialUnits: $days-oxygen * $FOOD_PER_DAY * $crewCapacity;
}
}
}
}
:parts ~#eva_kerbal ~.Module_ResourceCapacities {
@if $$crewCapacity > 0 {
+Module_ResourceCapacities {
+Data_ResourceCapacities {}
}
}
}
though for your case you might want to define this as such
@func klss-create-part-override-config($days-food:$POD_CAPACITY,$days-water:$days-food,$days-oxygen:$days-food) {
@return {
days-food: $days-food,
days-water: $days-water,
days-oxygen: $days-oxygen
};
}
So a person wanting to mod my mod would have to define a new config for each part they want to modify?
In this usage, yes, but this usage is specifically meant for modifying specific parts, if they want to modify a group of parts depending on a filter, they still will have to do a patch
Like my example use case for this is a mod author who made a command pod that is specifically meant to have more food than other command pods, but doesn't want a dependency on KLSS to just add the resources themselves
But there are other usages still, if you want modifyable constants you will only use in patches
The day length one for example was good
Ohhhh yeah that's a useful use case indeed. I'll need to do a proper documentation for my mod and modding my mod one day
Hmm also I'm wondering if "universal" stuff like day length & year length could be PM configs themselves, so that they are defined centrally and a RSS-RO mod wouldn't need to overwrite 25384 of them
The easiest way to do that, would be to have a json file in addressables that contains those default values (so yes built from unity)
Then to define a ruleset called something like :klss-config that will match that json file
couldn't a PM patch be made to create the file with a specific address?
It could yeah as well
I wasn't thinking about KLSS specifically, many mods might use day length in their patches and I was wondering if we could have a central way to config stuff like that
so you could also do
@new("label","name")
:json {
// your default values
}
so basically like global variables
Thats ... what this config system is meant to be, universal data
Somewhat variables, but I need to figure out the priority system for modifying them
and if I am going to allow them to be modified between stages, or only at the start
Yeah, but it'd be weird if KLSS was the mod defining day length for all other mods, maybe a "Community Constants" mod 🤔
Well once this system is implemented, I will look into it
it would probably fit in more with a Kopernicus-like mod
Yeah, no need to think abt this too hard until we have planet modding
So the reason I even came up with the config system, is I was thinking what if someone wanted to access other assets in a patch for like config or something, and I had the realization there is no technically feasible way to do that
Hmm I was also thinking: would an API that allowed mods to access configs programmatically be in the scope of PM?
The use case I'd have for this in KLSS would be defining the list of LS resources in a config so that it can be modified by other mods down the line (and this list is used in the code)
rn I do that with some variables defined in my plugin instance but it's pretty yucky
it might be best to do that using the addressables
like here
Thats where defining a generic json asset in addressables comes in
and just load the label from your code
and then other mods can patch the json under that label
Ohhh I see, yeah that's better
You might also want to put an
@patch "your-label";
At the top of the file where you define that, so other mods can patch it easier without needing to do that
And since PMs loading actions are always the first, you can do addressables loading actions to grab the data
lmao
And this is my example of a while loop lol
@function collatz-steps($n) {
$steps: 0;
@while $n != 1 {
$n: $n/2 @if ($n % 2) == 0 @else (3 * $n)+1;
$steps: $steps+1;
}
@return $steps;
}
Technically not an infinite loop
depending on the collatz conjecture
I was confused because in French (and nearly only French) this is called the Syracuse conjecture and not Collatz conjecture x)
Bruh
I think with this last commit these docs are definitely infinitely more useful than the previous documentation
Hmm, I should add an API into patch manager to somehow read configs from the Universe
Because you may want to define universally available data inside patch manager that is accessible outside of it
wouldn't the best solution for this simply be the addressable JSONs?
I suppose
but there still can be scenarios where you want to access something both inside and outside (at least readonly) I think
I can't personally think of any right now, but if you feel like it will be useful, go for it
(technically also the API will already be there, as you can use the builtin libraries as API yourself)
So I won't even have to write anything, as GetConfig and GetConfigs will both already be there
I'll have to create a section at some point called PatchManager interop with C#
another section that I just realized might be useful to have is making a custom PM module
I don't see why people would make custom modules specifically, what benefit does it provide if you are already making another SpaceWarpPlugin for your mod ... unless you aren't and then your mod's dll is just a custom module, but how would PM then know of the DLL?
I mean, for example a mod like Kopernicus might want a custom ":bodies #kerbin" syntax and transformations for it
and we have the module manager for the registration of custom modules
That doesn't require a custom module though, for example Community Resources already has something similar
This is how the ResourceRuleset is defined in PatchManager.Resources
but that uses the built-in resources module
oh lol
then the whole notion of modules is basically outdated
since the system was from the beginning meant mostly for 3rd party support
I mean the concept makes sense, if we want to load code that doesn't have an update loop or pollute game objects like spacewarp/bepinex/ksp2 would
I mean, even then, the module has to be registered from a plugin
unless you want Patch Manager to walk through all DLLs in the plugins folder and search for the right classes to register
Fair
I mean the module system is helpful for encapsulation of stuff though
using JetBrains.Annotations;
using PatchManager.Parts.Patchers;
using PatchManager.Shared.Modules;
using SpaceWarp.API.Loading;
namespace PatchManager.Parts;
/// <summary>
/// Part patching module.
/// </summary>
[UsedImplicitly]
public class PartsModule : BaseModule
{
public override void Init()
{
PartsUtilities.GrabModuleDataAdapters();
SaveLoad.AddFlowActionToCampaignLoadAfter<UpdateSavedVesselPartDefinitions>("Parsing parts text assets");
}
}
Like I use it here in patchmanager.parts
how does this get registered?
reflection
does PM just look at all loaded types with the attribute?
yeah I kinda thought so
lmao
my idea of third party interop in a lot of cases is providing attributes that allow reflection as compared to modules I suppose
But anywhoosle
static Universe()
{
RuleSets = new();
AllManagedLibraries = new();
_preloadedLabels = new();
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
// Only use public rule sets
foreach (var type in assembly.GetTypes())
{
if (type.IsAbstract || type.IsInterface) continue;
if (typeof(IPatcherRuleSet).IsAssignableFrom(type))
{
var rsAttribute = type.GetCustomAttribute<PatcherRulesetAttribute>();
if (rsAttribute != null)
{
RuleSets[rsAttribute.RulesetName] = (IPatcherRuleSet)Activator.CreateInstance(type);
_preloadedLabels.AddRange(rsAttribute.PreloadLabels);
}
}
var sassyLibraryAttribute = type.GetCustomAttribute<SassyLibraryAttribute>();
if (sassyLibraryAttribute != null)
{
var name = sassyLibraryAttribute.Mod + ":" + sassyLibraryAttribute.Library;
AllManagedLibraries[name] = new ManagedPatchLibrary(type);
Console.WriteLine($"Registered a managed library, {name}");
}
}
}
}
thats where that all gets created
personally I would still prefer to load these just from the registered modules, but I guess it doesn't really matter
I could but, why add that extra step imo? is there a good reason I'm unaware of?
not really
but then we should at least remove the PublicAPI attribute from the ModuleManager and make it internal
Gotcah
It still needs to be public as we use it in multiple assemblies though
we can remove [PublicAPI] though
or you can just add the assemblies to the already existing AssemblyInfo.cs in the project
The what?
Oh yeah
btw reading through the code, I realized that the modules were mostly a way to be able to control the life cycle of the 3rd party plugins
(plus they get displayed in the SW mod list, so that's a bonus)
but it's probably not needed for anything
I mean, it basically has the same life cycle as space warp plugins
yeah
I guess the only useful thing would be to see 3rd-party registered modules in the mod list entry for PM
and even then that's just a cosmetic thing
yeahh
alright, but sidestepping this whole tangent, this still stands, just not with a custom module but custom whatevers with the proper attributes
That's what the Patch Manager C# Interop Section is going to cover
How to use patch manager from within C#, and how to define custom libraries and rulesets
nice
I swear if someone adds a library to patch manager for downloading shit over the internet, I will strangle the computer
lmao that edit
I mean, I sandbox PM such that the only external effects it can have are the logs, and the addressables database
That just ruins that sandboxing
I also don't allow for nondeterministic code to be run, like Random
Anyways, I've decided, I really don't want to have to deal with the stage system (yet) for updating configurations
So I am going to add a priority system to it instead lol
Updating configs with a priority number feels kind of wack to me, but its the best way I can think of for now
But anyways:
https://pm.kerbal.wiki/config.html
Finishing off all the general syntax tutorials
I think now I can actually work on the PM 0.5.0 stuff in code, then finish the documentation and update the forum post
Munix, did you add the configs subfolder to patches in the template?
I'm adding some builtin's to the parts assembly
nope, forgot about that
but honestly I think it's fine, most people either won't know it even exists, and if they do, they'll know from the docs where you specifically say to put it there
I'll add it for the next release
Aight
Thoughts on this by the way?
seems pretty useful
And then also
nice
tbh we should just full-on call the patching language Sassy everywhere, I love it
lol
lol fair
omg I can't get over how simple the interop system I wrote is to use though
It automatically converts to and from DataValues for arguments and return types
and if you need specific things like the Universe, the Environment at the callsite, or the global environment of the patch, you can just add those as an argument
And you can also just do a VarArgs
or get all the arguments passed with List<PatchArgument>
or a Kwargs like in python
I think for now in the parts builtin, adding/finding/removing/replacing modules/module data should be useful enough for now.
This is the list of builtins I made for parts finally
@north mist should I add a part constructor such that I can add an export to patch in KSP2 Unity Tools?
how would you use it?
@new("part_name")
:parts {
// A lot of the fields will then be default initialized, there will be no modules or resources
}
And then in KSP2UnityTools, the button will work the same as export to json, but instead it will export to a .patch file with the default path being plugin_template/patches/%part_name%.patch, and instead of describing the modules in the ugly way with $type and whatever, it will use the plus syntax
sure, but what I mean is, what will it be useful for?
since to make a part you'll still need to build the addressables with it
Idk to have more readable part definitions in case people want to patch them was my thinking at first
ok, so if I understand correctly, basically the process would be the same as before, only with this, there would be no parts_data JSON in the addressables, instead it would be created (and cached) at runtime, and the prefab would be initialized with that
with the advantage being that the syntax is nicer
Yeah, so basically minimal advantage for the effort I suppose
But I'm gonna quickly convert a part to show what it would look like with a full part
I mean what this would mean is that you could possibly simply edit the patch file to make changes to your part, without needing to open Unity, right?
that might be useful in the dev process
Yeah, but the only issue would be manually copying stuff back over, but you can copy the cached json file from your PM cache
I feel like what would bring this to the next level would be if we had the possibility to fully create a part with any modules just from PM
so that you would only need to build the addressables with a model and textures, basically
and define the whole rest only in the patch file
like, having some sort of syntax for directly referencing assets (by address, for example) from patches, and then some Harmony backend patches where it would all be put together
basically KSP1-like part definitions, while the assets are still in addressables
that way, a modder could create a model and textures, build the addressables once, perhaps export a very baseline patch file from Unity, and then just work entirely within a text editor while iterating on the part definition, instead of having to go back to Unity
Why, all the asset names are based off of the part name
I feel like it would be a compromise between a Unity-only approach, and a "files on the disk"-only approach
well, not really, textures can be named anything you want
or no?
honestly it's been a minute since I've seen parts
lol
I'm assuming the textures can be named freely, since you just assign them to a new material, and assign the material to your prefab
and the prefab will contain those references no matter the name
I can even copy and paste the tooltips as comments into the patch file
another thing is we would need a way to be able to reference a module from a different module
Thats what all the part builtins are for tbh
yeah but those only return some transformed values, not the actual object reference, right?
which is what we'd need
We shouldn't need the actual object reference aaaaa, I forgot some modules did that :/
yeah, Engine and Gimbal are linked for example
I feel like that should be a harmony patch to just search for the gimbal when loading an engine, idk
but that can just be solved by adding some sort of a custom construct into the generated JSON, and then processing it in a Harmony patch
They shouldn't be linked
or that, tbh, even simpler
but, how do the Engine and Gimbal modules work for multi-nozzle engines?
like, the gimbal needs to be different for each nozzle to do a roll, for example
I wonder how the game deals with it, if it even does
I don't know if it does
You can't have multiple of the same module actually
that's... interesting
it doesn't seem to deal with it in any way in the config
I need to check obj_gimbal in the model
Here is an incomplete translation of a part to a patch file
yeah that looks great
I just need to add the constructor into PM
And then I'll work on adding the converter to KSP2UnityTools
Also uhhhh
yeah, that wasn't too hard
lmao
though I wonder if there are any other cases where it would be needed
I don't think I've seen any yet
honestly, why don't they just do that on their side lol
That was a screenshot of the Module_Engine class
christ on a stick
I'm going to wait to release any PM/KSP2UT update until after science tho
Cuz who knows what'll change
yeah
but this is pretty good news, so basically we'd only need to add asset addressing to it to be able to fully decouple the part definition from Unity
oh god
and an automatic loader and converter of .cfg files
bam
drop your KSP1 mod into BepInEx/plugins and you're done converting

it's free real estate

its 2.83+
Hello, I have been working on an addon for Blender to import and export .mu files. It is still very incomplete, but I have released it under the GNU General Public License (version 2), and it is available from github , with documentation here. NOTE: The addon is now targeted for Blender 2.83. Tho...
aksfjhsjgz this is what I get for not reading instructions
it's karma
of course it worked even on 3.4
huh so it just has obj_gimbal, obj_gimbal.001, obj_gimbal.002 and obj_gimbal.003
I wonder how the game deals with that
Decompile KSP
I'd love to get the docs to a point where other people can add their own tutorials
That would be cool, yeah
/// <inheritdoc />
/// <summary>
/// Create a new part asset
/// </summary>
/// <param name="dataValues">The arguments for the part asset, only one argument: name</param>
/// <returns>The part asset creator</returns>
public INewAsset CreateNew(List<DataValue> dataValues)
{
var name = dataValues[0].String;
var internalPartData = new PartData
{
partName = name,
angularDrag = 0,
coMassOffset = Vector3.zero,
coPressureOffset = Vector3.zero,
mass = 0,
maximumDrag = 0,
maxTemp = 0,
author = "",
bodyLiftOnlyUnattachedLift = false,
bodyLiftOnlyAttachName = "",
buoyancy = 0,
buoyancyUseSine = false,
breakingForce = 0,
breakingTorque = 0,
category = PartCategories.none,
family = "",
coBuoyancy = Vector3.zero,
coDisplacement = Vector3.zero,
oabEditorCategory = OABEditorPartCategory.NONE,
partType = AssemblyPartTypeFilter.Rocket,
cost = 0,
crashTolerance = 0,
crewCapacity = 0,
emissiveConstant = 0,
explosionPotential = 0,
fuelCrossFeed = false,
heatConductivity = 0,
inverseStageCarryover = false,
isCompound = false,
maxLength = 0,
radiatorHeadroom = 0,
radiatorMax = 0,
physicsMode = PartPhysicsModes.None,
sizeCategory = MetaAssemblySizeFilterType.XS,
skinMassPerArea = 0,
skinMaxTemp = 0,
skinInternalConductionMult = 0,
stageOffset = 0,
stageType = AssemblyPartStageType.None,
tags = "",
stagingIconAssetAddress = "",
attachRules = AttachRules.Defaults(),
attachNodes = new List<AttachNodeDefinition>(),
resourceContainers = new List<ContainedResourceDefinition>(),
resourceCosts = new List<PartResourceCostDefinition>(),
serializedPartModules = new List<SerializedPartModule>(),
resourceSummary = new SerializedResourceInfo(),
PAMModuleSortOverride = new List<PartsManagerCore.SerializedPartModuleDisplayOrder>(),
PAMModuleVisualsOverride = new List<PartsManagerCore.SerializedPartModuleDisplayVisuals>(),
AllowKinematicPhysicsIfIntersectTerrain = false
};
var internalJson = IOProvider.ToJson(internalPartData);
var internalJObject = JObject.Parse(internalJson);
var externalJObject = new JObject
{
["version"] = "0.3",
["useExternalData"] = false,
["data"] = internalJObject
};
return new NewGenericAsset("parts_data", name, new PartSelectable(externalJObject.ToString()));
}
Huh, I already had a system for this lmao
I do kinda remember having a similar conversation about something like this earlier
though I didn't know you implemented it
I don't remember doing it either
lmao
I suppose I could start working on the KSP2UnityTools side of things
This is going to be a fun one

