Modding:JSON Patching: Difference between revisions

From Vintage Story Wiki
m
Updated navbox to new content navbox.
(Marked this version for translation)
m (Updated navbox to new content navbox.)
(12 intermediate revisions by 4 users not shown)
Line 1: Line 1:
<languages/><translate>
<languages/><translate>
<!--T:1-->
<!--T:1-->
{{GameVersion|1.15}}
{{GameVersion|1.19.4}}


<!--T:2-->
<!--T:2-->
Line 10: Line 10:


<!--T:4-->
<!--T:4-->
Below is a basic guide to help you understand how patches are working. A [https://tools.ietf.org/html/rfc6902 full reference doc] is also available however. To make your patching live easier in the long term you can also use the ModMaker 3000™, which is a command line tool that ships with the game itself. If you modify vanilla assets, you can run ModMaker 3000™ to create a mod consisting of a set of patches based on the changes you made to the assets. You can then undo or reinstall the game to get your original files back.
Below is a basic guide to help you understand how patches are working. You can use the ModMaker 3000™ to start writing a patch. It is a a command line tool that ships with the game itself. If you modify vanilla assets, you can run ModMaker 3000™ to create a mod consisting of a set of patches based on the changes you made to the assets. You can then undo or reinstall the game to get your original files back. Note that before packaging the output of the ModMaker 3000™ in a mod, some edits should be made as described later in this document.
 
The patch syntax is based on [https://tools.ietf.org/html/rfc6902 rfc6902]. However, in addition to what's described in the RFC, it also has "addmerge" and "addeach" operations. The source code for the json patcher is [https://github.com/anegostudios/vsessentialsmod/blob/master/Loading/JsonPatchLoader.cs JsonPatchLoader.cs] and the customized [https://github.com/anegostudios/Tavis.JsonPatch/tree/0fc44b73c8412c561506745b1944fc18e88c1d83 Tavis.JsonPatch].


=== The basics === <!--T:5-->
=== The basics === <!--T:5-->


<!--T:6-->
<!--T:6-->
The first thing you need to do is to setup a generic mod as outlined on the wiki. That information can be found on the [[Mod Packaging|mod packaging]] page. In that example the domain is the name of your mod. Patching is considered a content mod but if you have a .dll or .cs in your mod it is a code mod. Patches go in assets/domain/patches.
The first thing you need to do is to set up a generic mod as outlined on the wiki. That information can be found on the [[Mod Packaging|mod packaging]] page. In that example the domain is the name of your mod. Patching is considered a content mod but if you have a .dll or .cs in your mod it is a code mod. Patches go in assets/domain/patches.


<!--T:7-->
<!--T:7-->
Now that the mod files and folders are setup it is time to make the first patch. Start by making a .json and name it however you'd like. Open the file with your favorite text editor and fill it in with the following.
Now that the mod files and folders are set up, it is time to make the first patch. Start by making a .json and name it however you'd like. Open the file with your favorite text editor and fill it in with the following.
<syntaxhighlight lang="json">
<syntaxhighlight lang="json">
[
[
Line 32: Line 34:


<!--T:9-->
<!--T:9-->
Now lets give an example. For these examples I'll be modifying the wolf-male.json because it was the first one I modified, I'm very familiar with it, and it provides a good basis for examples. In this first example, we'll modify wolf damage.
Now let's give a few examples, using the wolf-male.json file.  Note that specific line numbers etc. below might not match your own wolf-male.json file if you're using a different version of the game. In this first example, we'll modify wolf damage.


<!--T:10-->
<!--T:10-->
Line 42: Line 44:


<!--T:11-->
<!--T:11-->
You can change the domain to modify other peoples mods as well.
You can change the domain to modify other people's mods, as well.


<!--T:12-->
<!--T:12-->
Line 52: Line 54:
op: "replace"
op: "replace"
</syntaxhighlight>
</syntaxhighlight>
Since wolves already deal damage we simply want to replace the damage value.
Since wolves already deal damage, we simply want to '''replace''' the damage value.


<!--T:14-->
<!--T:14-->
Line 65: Line 67:


<!--T:16-->
<!--T:16-->
Within these square brackets, you'll find sections between curly braces. '''Curly braces { }'''
Within these square brackets, we have sections.  These start and end in curly braces. '''Curly braces { }'''


<!--T:17-->
<!--T:17-->
Line 71: Line 73:


<!--T:18-->
<!--T:18-->
Now to break down the example. We see the value we want to modify on line 52. To get to that we trace it back to the first label which is '''server'''. From there the next label is behavoirs. Here we notice the square brackets, so we must count. In this case to 5. Next is the label '''aitasks''', then we hit more square brackets. The count for this is 0 because that's the number you start on. Finally we get to the label '''damage'''.
Now to break down the example. We see the value we want to modify (damage) on line 119. Trace it back to the first label, which is '''server'''. From there the next label is '''behaviors'''. Here we notice the square brackets, so we must count -- in this case to 9.
* <code>{ code: "repulseagents"</code>... is section 0
* <code>{ code: "controlledphysics"</code>... is section 1
* <code>{ code: "despawn"</code>... is section 2
* <code>{ code: "health"</code>... is section 3
* <code>{ code: "deaddecay"</code>... is section 4
* <code>{ code: "floatupwhenstuck"</code>... is section 5
* <code>{ code: "harvestable"</code>... is section 6
* <code>{ code: "breathe"</code>... is section 7
* <code>{ code: "emotionstates"</code>... is section 8
* <code>{ code: "taskai"</code>... is section 9
 
Next is the label '''aitasks''', then we hit more square brackets. The count for this is 0 because that's the number you start on. Finally we get to the label '''damage'''.


<!--T:19-->
<!--T:19-->
Line 86: Line 100:
<syntaxhighlight lang="json">
<syntaxhighlight lang="json">
[  
[  
{ file: "game:entities/land/wolf-male", op: "replace", path: "/server/behaviors/4/aitasks/0/damage", value: 6},
{ file: "game:entities/land/wolf-male", op: "replace", path: "/server/behaviors/9/aitasks/0/damage", value: 6},
{ file: "game:entities/land/wolf-male", op: "add", path: "/drops/-", value: {  
{ file: "game:entities/land/wolf-male", op: "add", path: "/drops/-", value: {  
type: "item",  
type: "item",  
Line 101: Line 115:
<!--T:23-->
<!--T:23-->
For the value, there is an entire section showing that the value doesn't just have to be a number or text. In this case, we added the stick to the drops but by specifying a domain in front of stick like we do for the file it could be a stick from any mod!
For the value, there is an entire section showing that the value doesn't just have to be a number or text. In this case, we added the stick to the drops but by specifying a domain in front of stick like we do for the file it could be a stick from any mod!


=== Disabling Assets === <!--T:24-->
=== Disabling Assets === <!--T:24-->
Line 113: Line 125:
]
]
</syntaxhighlight>
</syntaxhighlight>
That's all you have to do. If there's already an enabled label in the file then we'd need to switch op from add to replace.
That's all you have to do. If there's already an enabled label in the file, then the "add" operation will replace it.


=== Targeting server or client assets ===
If you know that a target JSON file is only applied on the server or client, you can use the attribute "side" with the appropriate value to avoid unnecessary processing and the accompanying warnings in log files such as "''Hint: This asset is usually only loaded Server side''". For example, a cooking recipe will only be loaded by the server and you could therefore add the attribute :
<syntaxhighlight lang="json">
"side": "server"
</syntaxhighlight>
so the client doesn't try to patch anything.


=== File Path and Name Considerations === <!--T:28-->
=== File Path and Name Considerations === <!--T:28-->
By default, when you generate a mod using ModMaker 3000™, the folder structure will use '''game''' as the domain, and use the same file name as the vanilla JSON file. This can lead to mod conflicts.
By default, when you generate a mod using ModMaker 3000™, the folder structure will use '''game''' as the domain, and use the same file name as the vanilla JSON file. This can lead to mod conflicts.


Line 127: Line 145:
To avoid the possibility of mod conflicts, do not use the '''game''' folder. Instead, use a folder with the same name as your '''modid'''; that is, your mod's domain. So in this example, WolvesAreWimps.zip should instead have the folders '''assets\wolvesarewimps\patches''' and WolvesDropSticks.zip should instead have the folders '''assets\wolvesdropsticks\patches'''. File names are also arbitrary, you do not need to use '''survival-entities-land-wolf-male.json''' and can name the file anything you want, since its contents direct the game on what and how to patch, not the file's name.
To avoid the possibility of mod conflicts, do not use the '''game''' folder. Instead, use a folder with the same name as your '''modid'''; that is, your mod's domain. So in this example, WolvesAreWimps.zip should instead have the folders '''assets\wolvesarewimps\patches''' and WolvesDropSticks.zip should instead have the folders '''assets\wolvesdropsticks\patches'''. File names are also arbitrary, you do not need to use '''survival-entities-land-wolf-male.json''' and can name the file anything you want, since its contents direct the game on what and how to patch, not the file's name.
   
   
=== Overwriting issue === <!--T:31-->
=== AddMerge operation === <!--T:33-->
Patches that are creating e.g. behaviors or attributes (for example, if item does not have it), will be overwritten by patches from other mods that are doing same, but with another value.


<!--T:32-->
In addition to the "add" operation, the "addmerge" operation was added in version 1.19.4. "addmerge" is safer, and should be generally be used instead of "add.
For example, this patch will be overwritten with patch from example below, even if '''add''' operation is used:
 
They behave the same in many conditions:
* For targets that do not exist, both create the target
* For paths that end with '''dash -''', both will append a new entry to the target array
* For paths that end with a number, both will insert the value into the target array at that location
* For paths that point to an existing primitive value (string, bool, or int), both will replace the value
 
However, they differ when the target is an existing array or object. "add" will replace the target with the patch value. "addmerge" will append the patch value if the target is an array. "addmerge" will merge the value if the target is an object.
 
For example, prior to version 1.19.4, the fat item did not have a behaviors array. Let's say a mod added the behaviors through a patch (notice the path does not end in '''dash -'''):
<syntaxhighlight lang="json">
<syntaxhighlight lang="json">
// Bad patch example
[
[
   {
   {
     op: "add",
     "op": "add",
     path: "/behaviors",
     "path": "/behaviors",
     value: [{ "name": "SealPlacedCrock" }],
     "value": [{ "name": "SealPlacedCrock" }],
     file: "game:itemtypes/resource/fat.json"
     "file": "game:itemtypes/resource/fat.json"
   }
   }
]
]
</syntaxhighlight>
</syntaxhighlight>
However, in version 1.19.4 the vanilla fat object was changed to include a behaviors array.
<syntaxhighlight lang="json">
<syntaxhighlight lang="json">
// Section of fat.json from 1.19.4
        behaviors: [
                { name: "GroundStorable", properties: { layout: 'Quadrants', collisionBox: { x1: 0, y1: 0, z1: 0, x2: 1, y2: 0.125, z2: 1 }, scale: 0.3 } }
        ],
</syntaxhighlight>
So if the old patch was applied on the new game version, then it would replace the vanilla behavior instead of adding a new behavior.
<syntaxhighlight lang="json">
// Result of bad patch on 1.19.4
        behaviors: [{ "name": "SealPlacedCrock" }],
</syntaxhighlight>
Whereas, instead the patch could use the "addmerge" operation (putting aside that the "addmerge" operation didn't exist in 1.19.3).
<syntaxhighlight lang="json">
// Good patch example
[
[
   {
   {
     op: "add",
     "op": "addmerge",
     path: "/behaviors",
     "path": "/behaviors",
     value: [{ "name": "Placeable" }],
     "value": [{ "name": "SealPlacedCrock" }],
     file: "game:itemtypes/resource/fat.json"
     "file": "game:itemtypes/resource/fat.json"
   }
   }
]
]
</syntaxhighlight>
</syntaxhighlight>


If the target array exists (which it does in this example), then addmerge will append the patch array to the target array instead of replacing it.
<syntaxhighlight lang="json">
// Result of good patch on 1.19.4
        behaviors: [
                { name: "GroundStorable", properties: { layout: 'Quadrants', collisionBox: { x1: 0, y1: 0, z1: 0, x2: 1, y2: 0.125, z2: 1 }, scale: 0.3 } }
        ],
        behaviors: [{ "name": "SealPlacedCrock" }],
</syntaxhighlight>
Note that when the path ends with a number, "addmerge" will not merge the patch value into the existing array entry at that index. It will instead insert the patch value as a new entry at that index.
<!--T:34-->
'''Note''' : see the [[Modding:CompatibilityLib|Compatibility]] page for other ways to handle conflicts and dependencies between mods.
=== AddEach operation ===
"addeach" is used to insert multiple entries at some index in an array. As example, let's say a mod wanted to insert two new behaviors before the AnimationAuthoritative behavior in the hammer tool. This is the initial value before the patch:
<syntaxhighlight lang="json">
    // Section of hammer.json
behaviors: [{
name: "GroundStorable",
properties: {
layout: 'WallHalves',
wallOffY: 1,
sprintKey: true,
selectionBox: { x1: 0, y1: 0, z1: 0, x2: 1, y2: 0.1, z2: 1 },
collisionBox: { x1: 0, y1: 0, z1: 0, x2: 0, y2: 0, z2: 0 },
}
}, { name: "AnimationAuthoritative" }],
<syntaxhighlight lang="json">
The two new behaviors could be inserted using two "add" or "addmerge" patches.
<syntaxhighlight lang="json">
// Example patch using "add"
[
  // Insert new behaviors in the hammer item before the existing AnimationAuthoritative behavior.
  {
    side: "server",
    file: "game:itemtypes/tool/hammer", op: "add", path: "/behaviors/1",
    value: {
      name: "NewBehavior2"
    }
  },
  {
    side: "server",
    file: "game:itemtypes/tool/hammer", op: "add", path: "/behaviors/1",
    value: {
      name: "NewBehavior1"
    }
  },
]
</syntaxhighlight>
Or to be more concise, "addeach" could be used to insert both entries in the array with one operation. Note that when using "addeach", the entries to insert are in standard (non-reversed) order.
<syntaxhighlight lang="json">
// Example patch using "add"
[
  // Insert new behaviors in the hammer item before the existing AnimationAuthoritative behavior.
  {
    side: "server",
    file: "game:itemtypes/tool/hammer", op: "addeach", path: "/behaviors/1",
    value: [
      {
        name: "NewBehavior1"
      },
      {
        name: "NewBehavior2"
      }
    ]
  },
]
</syntaxhighlight>
"addeach" only works when the patch value is an array and the target is an array. Otherwise it will thrown an exception.


<!--T:26-->
<!--T:26-->
At this point, you have all the tools for modifying assets using the patching system. This is an incredibly powerful tool that only limited by your imagination. Have fun patching!
At this point, you have all the tools for modifying assets using the patching system. This is an incredibly powerful tool that is only limited by your imagination. Have fun patching!


<!--T:27-->
<!--T:27-->
{{Navbox/modding|Vintage Story}}
{{Navbox/contentmodding}}
</translate>
</translate>
Confirmedusers
536

edits