#Learning to use Patch Manager

1 messages · Page 1 of 1 (latest)

polar rock
#

I have a general sense of what patch manager is used for, which is modifying the internal functioning of the game. I don't really have a good understanding of what a patch even is though, let alone how one manages them. If anyone could help me figure out the basics I'd appreciate it!

lavish ridge
#

I'm also interested in learning Patch Manager as well!

feral saffron
#

basically, the game contains definitions for most things like celestial bodies, parts, experiments, tech tree nodes, etc. in JSON files stored in addressable bundles

#

assets can each have labels, and those are used by the game to retrieve them, and more than 1 asset can have the same label

#

so for example, all part definition JSON files have the label "parts_data", and the game loads all assets with that label when it's trying to create its internal database of parts

#

what Patch Manager does is that it allows you to target a specific label, go through all assets in it, and basically arbitrarily modify the values in the JSON

polar rock
#

Oooooooh

feral saffron
#

and also create or delete JSON files in labels

polar rock
#

I know there's a bunch of tools in #🔴tools-and-resources that I've never used for inspecting the game's contents, if I want to look through those JSON files do I use one of those tools or are they just part of my installation?

ripe lance
#

Text Asset Dumper, made specifically for this purpose

feral saffron
polar rock
#

Oh! Perfecttttt

feral saffron
#

so if you want to see for example all part definitions, you just dump the assets and go to BepInEx/plugins/TextAssetDumper/dump/text_assets/parts_data

#

and they will all be there

polar rock
#

Doing that right now

#

Wow that's a lot of json!!

lament knoll
lavish ridge
#

Interesting that the game is put together that way. Seems like that is very conducive for modding?

feral saffron
#

and to make the patching easier (because the structure of some of these JSON files is quite complex leading to rather cumbersome patches), Patch Manager also has some custom "rulesets" like :parts or :resources, which basically transforms the JSON files of specific type into a much easier hierarchy to work with within the patch file

polar rock
feral saffron
#

I think a large part of it is also because it's just really easy to export objects from Unity editor into it

lavish ridge
#

I've written a parser for .JSON files before so Im actually decently familiar with them

polar rock
#

in python you can literally just read in json and it autoconverts to a dictionary ahahahahahahaha

#

Okay, thanks a ton!

feral saffron
#

but there's also still a lot missing, so definitely ask anytime you want

polar rock
#

Okay!

feral saffron
polar rock
#

So, say I want to change a value within a map within a list

#

My idea for how the syntax for this might work is something like

#

but this isn't valid syntax apparently

feral saffron
#

oh yeah, I was just complaining to Cheese today that it's too bad this isn't possible lol

polar rock
#

ahh

feral saffron
#

could you show the part before that?

polar rock
#

I'm just trying to create a patch to test how this works

ripe lance
#

Why did I not make a list.set function aaaaa

#

but thats not necessary there

#
@use "builtin:dictionary";
@patch kerbin_science_regions;
:json #kerbin_science_regions {
  Regions[0]: $value:set(landedScalar, 1000.0);
}
feral saffron
#

dammit Cheese

#

faster

#

but wait

#

I didn't even know you can assign to a list index

#

in that case we definitely could add dictionary key assignment

lavish ridge
#

Learning to use Patch Manager for you too Munix

feral saffron
#

it's the same thing basically

ripe lance
feral saffron
#

excusemewhat

ripe lance
#

its just every time I've used the set function its been a dictionary in a list

feral saffron
#

ohhh

ripe lance
#

I suppose I could see about making nested indexers though

#

at least in values like that

#

but pain

polar rock
#

If I wanted to set the same value for every dictionary in the list, how would I do that? Do I have to make a function to create a loop or is there a shorter form?

feral saffron
#

so in that one patch, the map function could have been "simplified" to this?

$inclineMinmus: @function($body) {
    @if $body['GUID'] == 'Minmus' {
        $body['OrbitProperties']: $body['OrbitProperties']:set('inclination', 45.0);
    }
    @return $body;
};
ripe lance
feral saffron
ripe lance
polar rock
#

hmm that doesn't seem to actually change anything in game

ripe lance
#

check the logs and the PM cache

feral saffron
#

BepInEx/plugins/PatchManager/cache/kerbin_science_regions.zip

#

inside it you can find the modified JSONs, so you can check if the change was applied or not

polar rock
#

I don't see a zip with that name, only missions.zip

feral saffron
#

then there's an issue with the patch

#

the logs will probably tell you what

#

oh wait

#

I can't even find any label with the name kerbin_science_regions in the dump

#

ohhh I get it

#

it's the name of the asset

#

but you need to patch the label

#

aka the folder that the asset is in

#

that would be "science_region" in your case

polar rock
#

Ahhhhhhhhhhhhhhhhhhhh

feral saffron
#
@use "builtin:dictionary";
@use "builtin:list";

@patch science_region;

:json science_region {
  @if $$BodyName == Kerbin {
    Regions: $value:map(@function($item) { @return $item:set(landedScalar, 1000.0);});
  }
}
#

I think this is what you want

ripe lance
#

$$BodyName

#

otherwise thats always false

feral saffron
#

ah right

polar rock
#

Okay I am now beyond confused. This is the resulting "Regions" list in my cache. My Test.patch definitely has LandedScalar spelt correctly though, with the L capitalized and everything

feral saffron
#

is the file outside any mod?

#

well actually, shouldn't even matter

#

you need to enable Patch Manager cache purging

#

in Settings -> Mods -> Patch Manager

polar rock
#

Ohhhhhhhhhhh

feral saffron
#

it won't know to rebuild the cache unless a mod is installed, updated or removed

#

but you can configure it to always happen on game start

#

for development

polar rock
#

Okay I dumb, thankyouuu for helping

polar rock
#

If I want to patch the values for Laythe, which are in the folder Laythe.json, then I need to write @patch Laythe.json or just @patch Laythe?

ripe lance
#

This where you install shoemaker and do the following

#
:body #Laythe {
  // ...
}
polar rock
#

Even if i just want to add to LocalSimObjectsData?

#

Or well for laythe I guess i'd be creating that list

polar rock
#

@ripe lance Okay I tried not using shoemaker, patch manager seems to have tried to load the patch without error but nothing actually happened (like it didn't even generate anything in the cache) so I'm assuming that shoemaker is necessary to do anything at all with planets?

ripe lance
#

its not necessary, but hmm it sounds like you are doing something wrong

#

shoemaker just makes it a lot nicer

polar rock
#

ahh

#

I think the part of patch manager that I just do not understand is the stuff on the very first page of syntax stuff, the selection rules

#

so

#

that's probably where my issue is

#

This is how I'm attempting to add a list to the map called data under Laythe.json

ripe lance
#

yeah that won't work as is

polar rock
#

aaaa

ripe lance
#

@patch celestial_bodies for one

#

then :json #Laythe

polar rock
#

Why is it celestial_bodies? I thought it was supposed to be the name of the folder?

ripe lance
#

because that is the name of the folder, you'll find the same asset under that folder

polar rock
#

So the same asset can appear multiple times?

ripe lance
#

yes

polar rock
#

ahhhhhhhhhhhhh

#

Okay

#

And then, I'm trying to add an entry to a dictionary, so how would I do that?

feral saffron
#

you can just use myDict: myDict.set(key, value)

polar rock
#

I know set works

#

I was using it yesterday

ripe lance
#

@use "builtin:dictionary";

polar rock
#

I have that I'm pretty sure

#

This is my full patch

ripe lance
#

as the full signature of set is dictionary.set and it gets the type from the $value's type

#

plus you need the first argument

#

which is given by using $value

frozen lagoon
#

Sorry if this is dumb, but how do I select the J404 Panther engine? I tried using TextAssetDumper and go "engine_1v_turbofan_methane_panther" but whenever I use it, it doesn't work.

ripe lance
#

Whats the issue

#

Like it just wont select

frozen lagoon
#

idk

feral saffron
#

what do the logs say? is the part just not being patched at all?

frozen lagoon
#

//Module_Engine
:parts {
#engine_1v_turbofan_methane_panther > Data_Engine > engine_mode {
maxThrust: 1000.0;
}
}

#

here, sorry im dumb just started learning to program

#

Me and someone else who I wont say the name of are working on a mod that edits the engines to be their real-life counterparts

#

Didnt realise A Terrible Modder is doing something that is pretty similar

feral saffron
#

is he?

frozen lagoon
#

idk from the looks of it hes adding more engines as well

feral saffron
#

as far as I know, he's making a propeller engine part, not editing existing parts

frozen lagoon
#

oh

#

ok

feral saffron
#

this is an example from the forum thread

frozen lagoon
#

oh ok

frozen lagoon
#

So Ive mostly gotten what I wanted working but I do have one issue still

#

let me get the code rq

#

Ive tried a lot of different ways and looked through the wiki and asset dumps, but I just cant seem to find how to select only the afterburning mode (I am using the expanded version on purpose so later I can edit other parts and modules of parts

:parts {
       #engine_1v_turbofan_methane_panther 
       {
       * > Module_Engine {
            * > Data_Engine {
                * > #EngineModeID.["Wet"] {
                    maxThrust: 107.0;
                }
            }
        }
    }
}
#

This is the best I can get to so far

#

still doesnt do anything though

feral saffron
#

just engineMode[1] didn't work?

#

(assuming 0 is the normal and 1 is the afterburner)

frozen lagoon
#

gives me an error

#

yeah thats what I tried

#

this is where I got #EngineModID from btw

feral saffron
#

sorry, engineMode__s__[1]

frozen lagoon
#

ohhhhhhh

#

still gives me an error but I will test it out

feral saffron
#

well that tells you to use #ID

#

so like, Data_Engine > #Wet

frozen lagoon
#

ill see if that works next

feral saffron
#

(from the screenshot above)

frozen lagoon
frozen lagoon
stuck seal
#

I am new to patching and I believe I have a gross conceptural error about selecting. In the below image, the top patch works just fine while the bottom one is probably the N+1th iteration and I'm getting no where. I've also included the full context of the "rate" element I'm trying to patch from the cache zip. Am I not selecting through the Module arrays correctly or something?

feral saffron
#

that would be how you'd go about the patch if you were using the generic :json ruleset

#

but the specialized rulesets like :parts have some syntactic sugar and pre-processing done

#

so they don't exactly match the JSON structure

#

it's mostly to simplify some complex structures and long selectors

ripe lance
#

And otherwise impossible selectors

feral saffron
#

I believe your second patch would look more like this:

:parts #generator_0v_thermoelectric_radioisotope {
  * > Module_Generator
    * > Data_Generator {
      * > ResourceSetting {
        Rate: 1234;
      }
    }
  }
}
#

or, simplified:

:parts #generator_0v_thermoelectric_radioisotope > Module_Generator > Data_Generator > ResourceSetting {
  Rate: 1234;
}
stuck seal
#

Interesting. I want to say that learning had occurred, but tinkering is certinaly in progress.

feral saffron
#

there's just always too many things to do and too little time to write more documentation 😆

stuck seal
#

True enough. However, if you do put an example, you'll be my favorite person for the subsequent hour and forty five minutes or so. Depends on when the pizza arrives.

#

After some reading about Data_ModuleGenerator, this worked as expected. ```:parts #generator_0v_thermoelectric_radioisotope {

  • Module_Generator

    • Data_ModuleGenerator {
      ResourceSetting[Rate] : 1234;
      }
      }```

feral saffron
#

oh yeah, sorry, I didn't notice it's actually called Data___Module__Generator

#

weird

#

as far as I can tell, that's the only one that doesn't follow the convention

#

(PartComponentModule_XYZ -> Data_XYZ)

stuck seal
#

However, that omission was indirectly helpful since it sucked me further into the documentation.

lime edge
ripe lance
#

Yeah, it is

ornate heart
#

I'm trying to build a dictionary of <string, List<string>> but I just can't figure out how dictionaries work.

I've tried various things, but this is what I have currently:

$bodies-and-regions: {};

:discoverables {
    
    $body: $$BodyName;
    $log: debug-log("Starting body: " + $body);

    /* do I create a new dictionary kvp with this? */
    $bodies-and-regions: $value:set($body, []);
    
    @each $discoverable in $$Discoverables {
        $region-name: $discoverable["ScienceRegionId"];

        @if is-ignored($region-name) {
            $log: debug-log($region-name + " is ignored!");
        } @else {
            $log: debug-log($region-name + " is NOT ignored!");
        }

        /* append a new region (string) to the existing list for the current body ??? */
        $bodies-and-regions: $value:set($body, $value[$body]:append($discoverable[ScienceRegionId]));
    }

    /* Let's see what we have so far */
    @each $k, $v in $bodies-and-regions {
        $log: debug-log("Spice must flow");
        $log: debug-log("Key: " + $k);
        @each $reg in $v {
            $log: debug-log("Reg: " + $reg);
        }
    }
}

At the end of each file I'm doing logging to see what I have so far.
$bodies-and-regions is a top level variable so its data should persist through each discoverable file, right?
What I'm getting in the log is just the current body and its regions, so I guess I'm not creating new dictionary entries for each file but overriding the only entry in it? Or the data in $bodies-and-regions isn't persisting? Or something else?

ripe lance
#

Umm this is where the abstraction of PM being completely parallel gets in the way

#

I see the use in what you are trying to do, but it's going to take me a bit to think of a way to actually do it

ornate heart
#

Yeah, first noticed that when I wanted to do the logging in the end was when I did the logging outside of the :discoverables block, right after it, at top-level, so I'd log it only once. Then saw in the log that that part was executed very early on. So the order of commands in a patch file isn't executed linearly. That's ok, I figured I could solve that with staging.
But I didn't expect to see these logs writing only values for the current body.
What's happening exactly? The $bodies-and-regions variable is destroyed after each :discoverables pass I guess?

ripe lance
#

There is no way yet to do this, but I may allow it.

ornate heart
#

Oh I see, ok

ornate heart
#

@ripe lance, I assume I can't do:

:discoverables {
    @new($mission-id)
    :missions {
    ...
    }
}

Because I can't create a :mission object inside of a :discoverables scope, right? If so, I'll need to be able to create that top-level dictionary. Or I'll just type the list manually for now.

ripe lance
#

This is a lot of complex usage I wasnt planning for

#

The issue with stages here tho

#

Is the order of running

#

It goes first by label

#

Then by stag3

#

Not first by stage then label

ornate heart
#

I see. Alright, thanks for the input.

vagrant bluff
#

I have a problem that I think boils down to a problem with my patch file. After creating a module and attempting to load it, I get a sassypatch exception reading "Unknown part module Part_behavior_Class". Does anyone have any advice on where my problem may be?

#

:parts {
@if $$crewCapacity > 0 {
+Part_behavior_Class {
+Data_Class {
}
}
}
}

feral saffron
#

that sounds like your class isn't loaded in the game at the time of patching

#

are there any exceptions coming from your DLL plugin?

vagrant bluff
#

Would those exceptions be recorded in the Player.log file? If so, there does not appear to be any other exceptions relating to the mod.

feral saffron
#

yeah, they would

vagrant bluff
#

I am using the spacewarp template and using build and deploy. How would I go about seeing if the class has been loaded or ensuring that it gets loaded?

feral saffron
#

do you see your mod in the modlist?

vagrant bluff
#

yes