Modding Help How to avoid JSON patch conflicts? We need if/else…

Discussion in 'Starbound Modding' started by U.N.Owen, Nov 9, 2016.

  1. U.N.Owen

    U.N.Owen Big Damn Hero

    What should I do?
    If I simply insert the entire learnBlueprintsOnPickup, I'll make my mod incompatible with every mod that wants to do the same (learn some recipes after obtain) with this item.

    I want something like this:
    (actually it's JSON, but PHP highlighting is fine too)
    PHP:
    {
        
    "if" : [
            { 
    "op" "test""path" "/",
                
    "key" "learnBlueprintsOnPickup" //actually not supported in JSON patch
            
    },

            
    // it's already added by some mod:
            
    "op" "add""path" "/learnBlueprintsOnPickup/-",
                
    "value" "myitem"
            
    }
        ],

        
    "else" : [
            
    // vanilla:
            
    "op" "add""path" "/learnBlueprintsOnPickup",
                
    "value" : [ "myitem" ]
            }
        ]
    }
    However, we have no this option. JSON patch has a very poor options list.

    The problem has no solution?
    This kind of potential incompatibility is unavoidable?
     
  2. Inf_Wolf14

    Inf_Wolf14 Parsec Taste Tester

    I suggest you look into the "test" operation a little more for the patch system. You can easily test for a specific value, or a lack of that value and logically create patches that either apply or abort based upon the result of the operation.

    Its not an exact conditional statement like what you are referring to, but it isn't very different if you use it well.
     
    The | Suit likes this.
  3. U.N.Owen

    U.N.Owen Big Damn Hero

    Yes, we can.
    No, we can't.

    Wait…
    Now I found!
    Gross violation of ugly and useless RFC 6902. It's good.

    Now we have:

    First patch:
    PHP:
    [
        { 
    "op" "test""path" "/learnBlueprintsOnPickup""inverse" true },
        { 
    "op" "add""path" "/learnBlueprintsOnPickup",
            
    "value" : [ "myitem" ]
        }
    ]
    Second patch:
    PHP:
    [
        { 
    "op" "test""path" "/learnBlueprintsOnPickup"},
        { 
    "op" "test""path" "/learnBlueprintsOnPickup/0"
            
    "value" "myitem""inverse" true,
        },
        { 
    "op" "add""path" "/learnBlueprintsOnPickup/-",
            
    "value" "myitem"
        
    }
    ]
    Thanks.
    Now problem is almost solved.
    And I have only one question: How to combine them both in the same .patch file?
    Or I will have to move second patch in separate mod?
     
  4. bk3k

    bk3k Oxygen Tank

    Observe the patch I just made.


    Code:
    [
        [
            {
                "op" : "test",
                "path" : "/additionalItems",
                "inverse" : true
            },
            {
                "op" : "add",
                "path" : "/additionalItems",
                "value" : {}
            }
        ],
     
      [
            {
                "op" : "test",
                "path" : "/additionalItems/FCS",
                "inverse" : true
            },
            {
                "op" : "add",
                "path" : "/additionalItems/FCS",
                "value" : []
            }
        ],
     
      [
            {
                "op" : "add",
                "path" : "/additionalItems/FCS/-",
                "value" : "madtulip_the_any_key"
            }
        ],
     
      [
            {
                "op" : "test",
                "path" : "/scripts",
                "inverse" : true
            },
            {
                "op" : "add",
                "path" : "/scripts",
                "value" : [ ]
            }
        ],
     
      [
            {
                "op" : "add",
                "path" : "/scripts/-",
                "value" : "/scripts/bk3k/compatibility/additionalItems.lua"
            }
        ]
    ]


    And if that is confusing to you.. well first off copy/paste that into something like notepad++ with bracket matching etc. But that's an example of using patch batches. See this part

    Code:
      [
            {
                "op" : "test",
                "path" : "/additionalItems",
                "inverse" : true
            },
            {
                "op" : "add",
                "path" : "/additionalItems",
                "value" : {}
            }
        ],
    That's a single batch.

    Code:
      [
            {
                "op" : "test",
                "path" : "/additionalItems/FCS",
                "inverse" : true
            },
            {
                "op" : "add",
                "path" : "/additionalItems/FCS",
                "value" : []
            }
        ],
    different batch, same patch file. These batches happen sequentially. If the test condition for the 1st batch fails, only the first batch terminates. The second batch then runs. Then the 3rd. I built the path I wanted one level at at time. Testing for the existence at each level. It may be overly cautious in this exact case, but that should demonstrate very well what you're looking for.
     
    U.N.Owen and Inf_Wolf14 like this.
  5. U.N.Owen

    U.N.Owen Big Damn Hero

    Very big thanks!
    I intended that using multiple patchs in single file is possible, but could not find any documentation how to do it. You save me ^_^

    Now our if/else looks like this:
    PHP:
    [
        [
            { 
    "op" "test""path" "/learnBlueprintsOnPickup""inverse" true },
            { 
    "op" "add""path" "/learnBlueprintsOnPickup",
                
    "value" : [ ]
            }
        ],

        [
            { 
    "op" "add""path" "/learnBlueprintsOnPickup/-",
                
    "value" "myitem"
            
    }
        ]
    ]
    But now I have more questions.
    If we can do this, may be more difficult cases are solvable too?

    Case 1: Conditions for whole files.

    I want to do something like this (pseudocode):
    Code:
    if exists file.json then
        patch it
    else
        create it
    end
    
    For example, if file /liquids/alienjuice.liquid (or blood.liquid, ethanol.liquid, etc.) exists we can use it. Otherwise, we have to make our own alienjuice.liquid.
    It can be realized?

    Case 2: Dynamic expressions in patchs.


    How to get something like first free ID for immediate use? It's impossible?
    For example, I want to patch /collections/cooking.collection like this:
    PHP:
    [
       [
         { 
    "op" "add""path" "/collectables/glowcandy",
           
    "value" : {
             
    "order" null//placeholder
             
    "item" "glowcandy"
           
    }
         }
       ],
       [
         { 
    "op" "replace""path" "/collectables/glowcandy/order",
           
    "expression" "count(/collectables/*) + 1",
           
    "type" "integer"
         
    }
       ],
    ]
    Now, unfortunately, I just use "order" : 300 (not 151, because we have to keep reserve for vanilla future)

    [​IMG]
    Any idea how to make it better?
     
    Last edited: Nov 9, 2016
  6. bk3k

    bk3k Oxygen Tank

    I don't have anything to conditionally create a file. What I would suggest as a consolation prize is make unique file names and within them unique identifiers. The actual description the players see doesn't have to match the unique id. The lazy "good enough" way is like this.

    /liquids/UNOwen_alienjuice.liquid

    Now in the case where you expect something else, and want to change it but are worried that the load order may defeat you, another thing you can do in combination with the patch files is put in your _metadata file
    Code:
      "requires" : ["aMod_1", "aMod_2"],
      "includes" : ["aMod_3"],
    requires is going to make that other mod a requirement and the game will not load without it. If you're talking assets that your mod absolutely must have. Also your mod will load after the other thus you can ensure the patching happens. includes won't error out if the listed mod isn't present, but it will ensure that mod loads after the listed mods if present. There is a priority tag you can use too. The default "priority" : 0 if unset. I'm not sure that will account for /mods/ versus workshop, but requires and includes do. Setting your priority higher makes more sense if you are changing/adding files that you expect/prefer be modified by others later.

    For things that act more dynamically, you're just going to need to handle that on the LUA side which is pretty easy. Even if you need to read something from another object etc. I'll show you the script added by that patch example I provided.

    Code:
    sr_original_init = init
    
    function init()  --this is what I added
      local pos = object.position()
      local addItems = config.getParameter("additionalItems", {})
    
      for reason, group in pairs(addItems) do
        if reason == "FCS" then
          local temp = root.assetJson("/objects/ship/techstation/techstation.object")
          if not temp.uniqueId then  --this is removed by Madtulip's FCS mod, so that's how I know
            for _, i in ipairs(group) do
              world.spawnItem(i, pos)
            end
          end
        else
          for _, i in ipairs(group) do
            world.spawnItem(i, pos)
          end
        end
      end
      sr_original_init()
    end

    In theory I could have also done root.assetJson("/objects/ship/techstation/techstation.object:uniqueId") except that I expected that to not be present and if not present it would error out. I'm sure you're going to ask, but sadly I don't know an exposed function that would read out the contents of a directory.

    Anyhow you don't need to overly worry that you can't implement too many complex conditionals on the JSON side when the real work is done on the LUA side anyhow. The patches aren't as flexible as you'd like, but LUA certainly is.

    Now getting to collectables, I haven't paid any attention to them and don't know anything about them. It would be better that you didn't have to assign a number to them at all. For tracking purposes maybe a UUID that's used internally, and the numbering seen by the players generated dynamically/sequentially. Maybe the UUID isn't necessary because you already have an identifier that must be unique already. Make the identifier the key for a table of collectables rather than trying to make it a list. Well that's useless to give that advise to you, because you aren't a dev and they're the ones who implemented the system. But anyhow I mean.

    Code:
    collectables = {}
    collectables["glowcandy"] = true
    for _, entry in pairs(collectables) do
      stuff(entry)
    end
    
    etc
     

Share This Page