#Patch Manager

1 messages · Page 6 of 1

frail star
#

.

#

yeah i can make an update

#

👍

#

maybe i can even do it rn

#

in the car to school

#

when i get to class

upbeat hare
#

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

frail star
#

sounds good

#

in class

#

ill do it

#

did you fix the typo that was causing the antlr issue?

upbeat hare
#

Yeah

frail star
#

0.6

#

0.5.1

#

?

upbeat hare
#

Go with 0.6 I suppose

frail star
#

👍

#

publishing on hotspot wifii hits different nooooooooo

#

published!

#

i wonder if it shows up in the search

#

💀startup time

#

thats not happening

north mist
frail star
#

if you like the content i provide, please feel free to SMASH that star button

#

write a comment down below

north mist
#

but how do I subscribe

frail star
#

cant wait for my next project

#

"write the patcher lsp backend in zig"

upbeat hare
#

Why tho

#

zig is good but doesn't need to be used for everything

frail star
#

not true

#

ive been writing in only zig for the past couple months

#

i havent ran into a project i couldnt do with it

upbeat hare
#

Just because you can doesn't mean you should is my answer

north mist
#

ah yes can't wait to make a static website in zig

upbeat hare
#

Zig needs to be compiled and run on the end users computer

north mist
#

bruh

#

that is so extra

upbeat hare
#

typescript here can easily be just shipped

north mist
#

for no reason

#

lmao

frail star
#

binaries:::

upbeat hare
#

Yuck

frail star
#

wdym yuck 💀

upbeat hare
#

I said what I said

north mist
#

Cheese would never use a binary, ew, she compiles her Discord client and browser on her own machine munley

frail star
#

discord canary is ok

upbeat hare
#

Discord is written in javascript?

#

But I'm saying that binaries are eww to ship in an extension

frail star
#

electron nooooooooo

frail star
#

how terrible

north mist
#

use the technology best suited for the specific use case

frail star
#

lol bro, it was written as a proof of concept

#

its just to show it

north mist
#

is possible =/= is a great idea

#

no need to get all "lol bro" about it, I just said my opinion

frail star
#

apologies, "lol bro" might have been no the correct wording munley

#

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

north mist
#

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

frail star
#

....

north mist
#

not that the website itself is stupid

frail star
#

thats litearlly what i said

#

bruh

north mist
#

a proof of concept is cool

frail star
#

i never said, that you said, that the website is stupid

north mist
#

using it for everything just because it's possible is impractical

frail star
#

nobodies using it for everything 💀

north mist
#

you said you are

frail star
#

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

north mist
#

this is going absolutely nowhere

frail star
#

typescript is simply the example they provide you with

#

you can write it with anything

north mist
#

my whole point was just that not everything has to be done in zig

#

can we move on now?

frail star
#

nobody said that nooooooooo

#

sure

#

@upbeat hare gonna add workflows now 👍 have a minute free

#

workflows running for other project will take a while

frail star
#

interesting

upbeat hare
frail star
#

truly

frail star
#

interesting

#

finding new and funny ways to break antlr

upbeat hare
#

Wtf are you doing sinon

frail star
#

i’m cooking

#

damn

#

it didn’t stay a gif

#

discord didn’t like me copying that gif 💀

desert stirrup
#

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"

upbeat hare
# desert stirrup What's the proper way of selecting parts that have a specific module? I'm having...

.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

desert stirrup
#

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

upbeat hare
#

Alright, thats a bug

upbeat hare
desert stirrup
#

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";
                }
            }
        }
    }
}
upbeat hare
#

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...

desert stirrup
#

so $$Name selects only a field in the current scope?

#

makes sense

upbeat hare
#

Yes

desert stirrup
#

so I need to select the module itself

upbeat hare
#

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

desert stirrup
#

uhh, not for the faint of heart that is...

upbeat hare
#

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

desert stirrup
#
.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

upbeat hare
#
.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

desert stirrup
#

sounds good

upbeat hare
#

So it would end up something like

:parts {
    .Module_DataTransmitter :: {
        $commsRange: $$CommunicationRange;
    } {
        @if $commsRange > $MIN_RANGE {
            // ...
        }
    }
}
desert stirrup
#

Yep, that's perfectly understandable

upbeat hare
#

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

desert stirrup
#

oh yes

upbeat hare
#

This specifically shan't be too bad though

#

I understand specifically why this is necessary too

#

As the alternative syntax is ugly

upbeat hare
#

Ooo, this is going to be fun to add to the language

north mist
#

not that I have any better idea right now, but I kinda hate the { ... } { ... } syntax

#

🙈

upbeat hare
north mist
#

I agree, but then I'd at least add another separator between the braces or something

#

this looks kinda confusing

upbeat hare
#

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

north mist
#

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?

upbeat hare
#

Maybe yeah

#

I guess I'll change it to a single colon

north mist
#

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

upbeat hare
#

I disagree as there is no other case where you can assign variables and do arbitrary code inside parentheses

north mist
#

or, square brackets like when using captures in C++ lambas?

upbeat hare
#

I guess that may work

#

The parser treats the inside of the stuff like a function body but, yeah that should be fine

north mist
#
:parts {
    .Module_DataTransmitter:[$commsRange: $$CommunicationRange] {
        @if $commsRange > $MIN_RANGE {
            // ...
        }
    }
}
#

I do think that has much better readability

upbeat hare
#

I'd rather keep the semicolon, it makes parsing a lot easier in the case of multiple captures

north mist
#

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

upbeat hare
#

Also, community resources likely is going to have to be rebuilt after this update

upbeat hare
#
: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 {
            // ...
        }
    }
}
north mist
#

that looks good 👍

upbeat hare
#

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]);
}
upbeat hare
#

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

upbeat hare
#

@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

north mist
#

done

#

oh well

north mist
#

lol

upbeat hare
#

Nah I wouldn't've had to anyways

#

guess imma release PM 0.4.0 then

north mist
#

btw you should also be able to push packages, since you have the API key

upbeat hare
#

Oh yeah

north mist
#

dotnet nuget push file.nupkg -s https://nuget.spacewarp.org/v3/index.json -k API_KEY

upbeat hare
#

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)

north mist
#

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 😆

north mist
#

that was a lot of changes indeed

upbeat hare
#

Could you put an approval on the PR then?

north mist
#

yeah lmao sorry, forgot

desert stirrup
#

oh it's already released, very nice

upbeat hare
#

Also @frail star is going to have to update the grammar file for the vscode extension

upbeat hare
#

So you are using this mod as a tool for your orbital survey mod, makes sense

desert stirrup
#

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 🙂

upbeat hare
#

Awesome!

upbeat hare
#

I wonder if I can have patch manager run on multiple threads

stiff crater
upbeat hare
#

those words sound scary

desert stirrup
upbeat hare
#

I should give you perms to edit it

north mist
#

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

upbeat hare
#

Ohh you just reminded me I completely forgot to write the forum post

upbeat hare
#

@north mist how does this look for a forum post

north mist
#

looking good, though, being a bit of a grammar nazi, I'd fix the "it's purpose" to "its purpose" in the description

upbeat hare
#

Alright, done

#

So, I'm good to post this?

north mist
#

sure

upbeat hare
#
: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

north mist
#

it will definitely become useful when we start getting deeper into mods patching mods patching stock stuff etc.

upbeat hare
#

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

north mist
#

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

upbeat hare
#

Yeahh

#

One note all the implied stages can also just be referenced by their mod id as well

stiff crater
#

Does PM have a ternary conditional operator?

upbeat hare
#

Yes actually

#

Inline if else

stiff crater
#

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)

upbeat hare
#

x @if y @else z (idk jf i removed the ats or not

#

@stiff crater correction
x if y else z

upbeat hare
#

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

upbeat hare
#

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

upbeat hare
#

Thoughts?

north mist
#

yeah I think that's fine

desert stirrup
#

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

upbeat hare
# desert stirrup What's a local and what's a global stage? First I understood it as: global stage...

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

desert stirrup
#

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)

desert stirrup
upbeat hare
#

fixed

upbeat hare
#

Ill likely transcribe what I wrote here to the wiki at some point

desert stirrup
#

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.

upbeat hare
#

Go for it

desert stirrup
#

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?

upbeat hare
#

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

desert stirrup
#

so @after 'A' would have 00001 and @after 'A:post' would have 09996 ?

upbeat hare
#

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

desert stirrup
#

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"

upbeat hare
#

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

desert stirrup
#

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... 🙂

upbeat hare
#

Well mod B as a whole doesnt patch after A, its individual patches do

desert stirrup
#

I get that, in this example mod B wants to do an @after for mod A

upbeat hare
#

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

north mist
#

theoretically, does this system even need any numbers at all? couldn't the patch priorities be stored in some sort of a priority graph?

upbeat hare
#

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

north mist
#

it would save you the trouble of doing all this math and then possibly having to do resizing when someone runs into issues

upbeat hare
#

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?

north mist
#

yeah I think that should be enough

upbeat hare
#

As stages are just named graph nodes

north mist
#

the issue with MM was that there were only predefined ones

#

stuff like :BEFORE, :AFTER, and :FINAL

upbeat hare
#

Ahhh yeah

#

@desert stirrup ... I hate to say it but all of what I described may become a teensey weensey bit outdated

desert stirrup
#

that's perfectly fine, as long as we're improving on things 🙂

north mist
#

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"

desert stirrup
#

and mod C needs its patch to run before B, but after A

upbeat hare
#

And thats where named stages will be

@define-stage "AofB" {
    @before "B";
}

Itll be more complex syntactically as its just a bunch of relations

desert stirrup
#

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

upbeat hare
#

@require/@require-not

desert stirrup
#

ah yes, missed it

north mist
#

can those be composed into more complex relations?

upbeat hare
#

Yes

#

You can make one that is before "AofB"

north mist
#

like "require mod A but only if B and C are not installed"

upbeat hare
#

Umm ... that is a complex relation

#

Aaaa

north mist
#

I'm realistically trying to think of the various patches I remember seeing for example for RP-1

upbeat hare
#

Maybe I could make @require do boolean logic

north mist
#

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

upbeat hare
#

Id literally have to introduce boolean logic for that

#

@require "(B || C) || (!(B || C) && A)"

#

Is what you just told me

#

Wait

north mist
#

or, in the spirit of SCSS, more like

@require 'modA' and not ('modB' or 'modC');
desert stirrup
#

or keywords if Patch Manager is more targeted for non-programmers (B or C) or (not(B or C) and A)

upbeat hare
#

Yeah

#

Also no semicolon, id rather it implicitly end

north mist
#

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)

upbeat hare
#

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

desert stirrup
#

I'm not using it

north mist
#

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

desert stirrup
#

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) ?

north mist
#

yeah the 2nd one definitely

upbeat hare
#

not > is harder to parse than not (...);

north mist
#

you wouldn't write ($commRange ! > $minCommRange)

upbeat hare
#

Well not too much harder, but a bit uglier

desert stirrup
#

right

upbeat hare
#

Anyways, working on the syntax for require expressions

desert stirrup
#

I'm just thinking as a non-programmer. if this value is not larger than this other value

upbeat hare
#

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

desert stirrup
#

alright

upbeat hare
#

(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

north mist
#

well if you want stuff taught in schools, you could do @require 'modA' ∧ ¬('modB' ∨ 'modC');

upbeat hare
#

Okay, but not everyone has a boolean logic class either

north mist
#

Unicode operators sound fun

#

lmao

upbeat hare
#

I may have had such a class but my degree requires it

north mist
#

eh I mean this is like high school junior year level stuff

upbeat hare
#

With those symbols? nope

#

Have you been to an american high school munix

north mist
#

true munley

upbeat hare
#

Honestly, with this I can get rid of @require-not

@require-not "mod"
; and
@require not "mod"

Mean the exact same thing

north mist
#

yeah, I was thinking that as well

upbeat hare
#

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

north mist
#

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?

upbeat hare
#

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"
};

?

north mist
#

yeah I think that looks cleaner

desert stirrup
#

looks good

upbeat hare
#

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?

north mist
#

yeah I do agree, we want patches to be as "extensible" by other patches as possible

upbeat hare
#

Yeah

#

So every single possible stage for a patch has a known name

north mist
#

yeah, this change alone makes a huge improvement over MM

desert stirrup
#

You'd need to define the stage only if you're targeting another stage or always?

north mist
#

ideally always, so that another patch can target any of your patch stages

desert stirrup
#

yeah, was thinking that aswell

upbeat hare
#

The implicit stage name of any patch is mod_id, but you likely want to make that more granular

north mist
#

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

desert stirrup
#

👍

#

and if mod A and mod B both target the same stage from mod C, what determines the execution order?

upbeat hare
#

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

desert stirrup
#

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

upbeat hare
#

and if a:pre-c does not exist, then this constraint is ignored

upbeat hare
#

if they mess with the same area in a possibly incompatible way, then they have to have explicitly defined compatibility

desert stirrup
#

Would alphabetical execution be preferable to a random execution?

upbeat hare
#

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

north mist
desert stirrup
upbeat hare
upbeat hare
#

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

north mist
#

yeah, I would leave the order of patches undefined on the same-stage level

upbeat hare
#

Does that look odd munix?

north mist
#

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

upbeat hare
#

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

north mist
#

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

upbeat hare
#

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

north mist
#

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

upbeat hare
#
@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?

north mist
#

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 {...}
}
north mist
north mist
#

semantically they seem like the same thing, basically just with or without brackets

upbeat hare
#

@require was going to work on mod guids only

#

not stages

north mist
#

ah

upbeat hare
#

I can't have stage definitions be dependent on the existence of other stages, or else that is going to be a nightmare

north mist
#

yeah, I can imagine, well, then disregard exists-stage, and just replace that with exists-mod in my examples

upbeat hare
#

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)

north mist
#

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

upbeat hare
#

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)

north mist
#

yeah

upbeat hare
north mist
#

yeah, sure, it's only a shorthand anyway

#

and yeah it does not look like something that would have a lot of uses

upbeat hare
#
[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

north mist
#

awesome

upbeat hare
#

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

north mist
#

wtf

upbeat hare
#

And it literally has to run

north mist
#

what if you just try Debug.Log?

upbeat hare
#

Trying that now

#

And now buildandrun is being fun and not working

north mist
#

have you tried deleting build and dist?

#
  • Nuget restore
upbeat hare
#

Nope

#

Plus that Debug.Log fix literally did nothing

upbeat hare
#

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

upbeat hare
#

Finally

#

Except, it doesn't show what stages were sorted :/

upbeat hare
#

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?

north mist
#

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?

upbeat hare
#

Why would that matter?

north mist
#

actually no that makes no sense

#

I guess you can just move it

#

if something breaks, we'll notice it anyway lol

upbeat hare
#

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

north mist
#

ah yeah

#

OnPreInitialized happens already in a flow action

#

what was the change that requires the initialization to be moved, anyway?

upbeat hare
#

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)

north mist
#

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

upbeat hare
#

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

north mist
#

I can tell you that getting paid for it doesn't feel that much better

upbeat hare
#

Fair

#

I'm gonna spend my freetime tomorrow going over the wiki and the PM docs

north mist
#

I guess I should do my part and at least write the docs for UITK and LFO

upbeat hare
#

(am watching a movie rn)

upbeat hare
#

Okay, time to write a bunch of empty tutorial blocks with [TBD] under them so I know which ones I need to write

north mist
#

I think the UITK docs are a good opportunity to try using Writerside for a project

upbeat hare
#

I'm genuinely interested

upbeat hare
#

Screw it i might try using writerside as well it may help me organize better

upbeat hare
#

Oh I really like this

north mist
#

yeaah I love the tabs

upbeat hare
#

Oh gods

#

We are going to have to update the VSCode extension ... again

upbeat hare
#

Not me recommending people to install VSCode for Patch Manager

stiff crater
#

How hard would converting the VS Code extension into a Rider plugin be? 🤔

upbeat hare
#

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)

north mist
#

I was wondering if this isn't a bit out of scope lmao

upbeat hare
#

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

north mist
#

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

upbeat hare
#

Yeah

north mist
#

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

upbeat hare
#

oh gods no

north mist
# stiff crater How hard would converting the VS Code extension into a Rider plugin be? 🤔

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

GitHub

A sample plugin for jetbrains IDEs that uses an ANTLR grammar for a nontrivial custom language. - GitHub - antlr/jetbrains-plugin-sample: A sample plugin for jetbrains IDEs that uses an ANTLR gramm...

#

the rest of the extension would mostly have to be completely rewritten

upbeat hare
#

Just me casually linking semver.org when describing what the version field is

north mist
#

please and thank you, everyone should know it

#

👀

upbeat hare
#

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

north mist
#

hm the libraries folder is in the root?

upbeat hare
#

Yes

#

It is in every mod based off of PM so far

north mist
#

I kinda dislike that

#

wouldn't patches/libraries be better?

#

to have all PM stuff in one folder

upbeat hare
#

I suppose, I mean it wouldn't be hard to subtly guide a better structure yeah

north mist
#

because <mod_root>/libraries kinda makes me think it should contain 3rd party DLLs that the mod loads

upbeat hare
north mist
#

thank you 😆

#

does Patch Manager explicitly search those folders, or does it just look for all .patch files anywhere in the hierarchy?

upbeat hare
#

It'll look everywhere under a mods hierarchy

#

I'm just trying to subtly encourage organization

north mist
#

ok, yeah, then I think it's definitely better to have a single common folder for all of them

north mist
#

it was one of the main reasons I made the mod templates

#

lmao

upbeat hare
#

I am likely going to create some form of PM template for somewhere at some point, but like its 1 file and 2 folders

north mist
#

which reminds me, I could update the template to create those folders automatically for all mods

upbeat hare
#

Or yeah, just make the patch folders yeah

north mist
#

since I already create the full hierarchy for stuff like addressables, images, etc

upbeat hare
#

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)

north mist
#

I saw, that's great

upbeat hare
#

And now Patch+Code is completed

#

Patch+Code+Parts is going to be synthesis of the two

north mist
#

I intend to release the new 1.6 templates today or at latest tomorrow

upbeat hare
#

And these docs wont be done before then

#

Also if anyone else wants to work on the docs with writerside

north mist
#

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

upbeat hare
#

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

north mist
#

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

upbeat hare
#

I think it should best I can tell

#

I suppose I could try making an addon quickly lol

north mist
#

(by the way if not, SCSS syntax highlighting works pretty well in most cases)

upbeat hare
north mist
#

hm

#

in that case I guess CSS still kinda works as a fallback

#

though it will definitely have some issues

upbeat hare
#

Well, its time to write some java

north mist
upbeat hare
#

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

north mist
upbeat hare
#

Okay the setup instructions for that don't even work, nevermind

north mist
#

yeah that specific thing is like 5 years old...

#

so it's possible it's just completely outdated by now

upbeat hare
#

hmmm

north mist
#

yeah the official way is to use a BNF grammar

upbeat hare
#

This looks like a helluva lot of pain

#

Another day

upbeat hare
#

Time to write a few more pages of documentation today, aaaaa

upbeat hare
#

I definitely like how Writerside renders tables

upbeat hare
#

Awesome, I can change the depth of the sidebar

upbeat hare
#

Almost done writing the tutorial page on values, variables, and expressions

#

this is so damn long

upbeat hare
#

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?

north mist
#

would those be mutable or immutable?

stiff crater
upbeat hare
north mist
#

one scenario where I can imagine something like this being useful is the same use case as CSS variables

upbeat hare
north mist
#

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

upbeat hare
#

Well the default values would exist at the beginning before the stage progression

north mist
#

or that, sure

upbeat hare
#

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?

stiff crater
#

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

upbeat hare
#

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

upbeat hare
#

But that'd require some work in itself

upbeat hare
#

@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

upbeat hare
stiff crater
upbeat hare
#

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

upbeat hare
#

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

north mist
#

sure

upbeat hare
#

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

upbeat hare
#

I just thought of the best way to think of top level statements that aren't selection blocks

#

Like a preprocessor

#

kind of

upbeat hare
#

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?

upbeat hare
#

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

north mist
#

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

upbeat hare
#

Yes, actually

#

That is intended

#

I know it can look confusing, which is why I keep using variables with - in them

north mist
#

what if I had

$a: 10;
$a-1: 5;
$b: $a-1;
upbeat hare
#

Though those docs are wrong

north mist
#

is b equal to 9 or 5?

upbeat hare
#

there should be a space in the $b: $a-11

#

let me fix that and put a warning in the docs

north mist
#

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

upbeat hare
#

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

north mist
#

yeah I'm not a big fan of this whole mess either tbh

upbeat hare
#

I don't have space separated lists for a reason

north mist
#

yeah I suppose it should be fine if all the operator examples actually have spaces around them

#

just to make it clear

upbeat hare
#

I will do that yeah

north mist
#

(theoretically you could also disallow the first character that comes after a dash in a variable name to be a digit)

upbeat hare
#

Honestly, that'd be nice, and also disallow it to be the last character

#

give me one sec, I can do that

north mist
#

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 👀

upbeat hare
#

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
north mist
#

honestly just allowing [a-zA-Z] would probably be fine, too

upbeat hare
#

Thats what I changed the grammar to parse

#

So

$a-5

Will be parsed as $a - 5

north mist
#

yeah that sounds good

upbeat hare
#

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

upbeat hare
#

Note not every page is finished or even startred on

#

why does module manager have space separated arrays

north mist
#

the syntax mostly comes from the KSP config files

#

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 munley

upbeat hare
#

Yeah, thats just ugly

#

and the fact that MM has separate syntax for , and separated arrays

upbeat hare
#

Hmm, I have another idea
I should make @use "builtin"; include all builtin libraries

north mist
#

analogously, @use "modName" could include all libraries from a mod

upbeat hare
#

(if you don't already have a library in your mod named modName)

stiff crater
#

Wouldn't @use "builtin:*"; be more explicit? Also what happens if there are symbol name conflicts for use statements?

upbeat hare
#

Actually, thats a good idea

upbeat hare
#

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

north mist
#

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

upbeat hare
#

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

north mist
#

oh that's pretty cool

upbeat hare
#

The same is true for functions as well

#

The default arguments are all calculated at call time

upbeat hare
#

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);
}
stiff crater
upbeat hare
#

Lets copy a simplified version of one of your patches

upbeat hare
# stiff crater How would the result of such a function be used in a patch?
@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 {}
        }
    }
}
upbeat hare
stiff crater
upbeat hare
#

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

stiff crater
#

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

upbeat hare
#

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

north mist
upbeat hare
#

It could yeah as well

stiff crater
#

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

upbeat hare
#

so you could also do

@new("label","name")
:json {
  // your default values
}
north mist
upbeat hare
#

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

stiff crater
upbeat hare
#

Well once this system is implemented, I will look into it

north mist
#

it would probably fit in more with a Kopernicus-like mod

stiff crater
#

Yeah, no need to think abt this too hard until we have planet modding

upbeat hare
#

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

stiff crater
#

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

north mist
#

it might be best to do that using the addressables

upbeat hare
#

Thats where defining a generic json asset in addressables comes in

north mist
#

and just load the label from your code

#

and then other mods can patch the json under that label

stiff crater
#

Ohhh I see, yeah that's better

upbeat hare
#

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

upbeat hare
north mist
#

lmao

upbeat hare
#

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

stiff crater
upbeat hare
#

I think with this last commit these docs are definitely infinitely more useful than the previous documentation

upbeat hare
#

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

north mist
#

wouldn't the best solution for this simply be the addressable JSONs?

upbeat hare
#

I suppose

#

but there still can be scenarios where you want to access something both inside and outside (at least readonly) I think

north mist
#

I can't personally think of any right now, but if you feel like it will be useful, go for it

upbeat hare
#

(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#

north mist
#

another section that I just realized might be useful to have is making a custom PM module

upbeat hare
#

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?

north mist
#

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

upbeat hare
#

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

north mist
upbeat hare
north mist
#

oh lol

#

then the whole notion of modules is basically outdated

#

since the system was from the beginning meant mostly for 3rd party support

upbeat hare
#

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

north mist
#

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

upbeat hare
#

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

upbeat hare
#

reflection

north mist
#

does PM just look at all loaded types with the attribute?

#

yeah I kinda thought so

#

lmao

upbeat hare
#

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

north mist
#

personally I would still prefer to load these just from the registered modules, but I guess it doesn't really matter

upbeat hare
#

I could but, why add that extra step imo? is there a good reason I'm unaware of?

north mist
#

not really

#

but then we should at least remove the PublicAPI attribute from the ModuleManager and make it internal

upbeat hare
#

Gotcah

#

It still needs to be public as we use it in multiple assemblies though

#

we can remove [PublicAPI] though

north mist
#

or you can just add the assemblies to the already existing AssemblyInfo.cs in the project

upbeat hare
#

The what?

north mist
#

this thing

#

in Properties

upbeat hare
#

Oh yeah

north mist
#

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)

north mist
upbeat hare
#

I mean, it basically has the same life cycle as space warp plugins

north mist
#

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

upbeat hare
#

yeahh

north mist
upbeat hare
#

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

north mist
#

nice

upbeat hare
#

I swear if someone adds a library to patch manager for downloading shit over the internet, I will strangle the computer

north mist
#

lmao that edit

upbeat hare
#

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

north mist
#

aw and I was looking forward to introducing race conditions from a PM patch

upbeat hare
#

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

upbeat hare
#

Updating configs with a priority number feels kind of wack to me, but its the best way I can think of for now

#

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

upbeat hare
#

Munix, did you add the configs subfolder to patches in the template?

upbeat hare
#

I'm adding some builtin's to the parts assembly

north mist
#

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

upbeat hare
#

Aight

upbeat hare
north mist
#

seems pretty useful

upbeat hare
#

And then also

north mist
#

nice

#

tbh we should just full-on call the patching language Sassy everywhere, I love it

#

lol

upbeat hare
#

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.

upbeat hare
#

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?

north mist
#

how would you use it?

upbeat hare
#
@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

north mist
#

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

upbeat hare
#

Idk to have more readable part definitions in case people want to patch them was my thinking at first

north mist
#

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

upbeat hare
#

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

north mist
#

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

upbeat hare
#

Yeah, but the only issue would be manually copying stuff back over, but you can copy the cached json file from your PM cache

north mist
#

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

upbeat hare
north mist
#

I feel like it would be a compromise between a Unity-only approach, and a "files on the disk"-only approach

north mist
#

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

upbeat hare
#

I can even copy and paste the tooltips as comments into the patch file

north mist
#

another thing is we would need a way to be able to reference a module from a different module

upbeat hare
#

Thats what all the part builtins are for tbh

north mist
#

yeah but those only return some transformed values, not the actual object reference, right?

#

which is what we'd need

upbeat hare
#

We shouldn't need the actual object reference aaaaa, I forgot some modules did that :/

north mist
#

yeah, Engine and Gimbal are linked for example

upbeat hare
#

I feel like that should be a harmony patch to just search for the gimbal when loading an engine, idk

north mist
#

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

upbeat hare
#

They shouldn't be linked

north mist
#

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

upbeat hare
#

I don't know if it does

north mist
#

how does KSP1 deal with it actually

#

let me check the .cfg for the Mammoth

upbeat hare
#

You can't have multiple of the same module actually

north mist
#

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

upbeat hare
north mist
#

yeah that looks great

upbeat hare
#

I just need to add the constructor into PM

#

And then I'll work on adding the converter to KSP2UnityTools

north mist
#

ahhhh

#

I need Blender 2.x for the KSP .mu addon

#

this is annoying

upbeat hare
#

Also uhhhh

north mist
#

lifesupport 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

upbeat hare
#

I'm saying that is on their side

#

I didn't add that

north mist
#

ohh

#

bruh

upbeat hare
#

That was a screenshot of the Module_Engine class

north mist
#

so, basically the prefab doesn't need the reference at all?

upbeat hare
#

Nope

#

lol

north mist
#

christ on a stick

upbeat hare
#

I'm going to wait to release any PM/KSP2UT update until after science tho

#

Cuz who knows what'll change

north mist
#

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

upbeat hare
#

We should add a .mu loader into PM

#

(gods no)

north mist
#

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

upbeat hare
#

KSP1 modders would love that

#

KSP2 players would hate that

north mist
#

bruh I installed 2.93 and the addon still doesn't work

#

I think I need 2.7

upbeat hare
#

its 2.83+

north mist
#

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

upbeat hare
#

Decompile KSP

upbeat hare
#

I'd love to get the docs to a point where other people can add their own tutorials

north mist
#

That would be cool, yeah

upbeat hare
#
/// <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

north mist
#

I do kinda remember having a similar conversation about something like this earlier

#

though I didn't know you implemented it

upbeat hare
#

I don't remember doing it either

north mist
#

lmao

upbeat hare
#

I suppose I could start working on the KSP2UnityTools side of things

#

This is going to be a fun one

upbeat hare
#

gtg for dnd tho

#

As I said, this is going to be fun