1. Trouble with the game?
    Try the troubleshooter!

    Dismiss Notice
  2. Issues with the game?
    Check the Known Issues list before reporting!

    Dismiss Notice
  3. Before reporting issues or bugs, please check the up-to-date Bug Reporting Thread for the current version.
    0.38 Bug Reporting thread
    Solutions and more information may already be available.

Slots2 denyTypes argument is not checked in UI

Discussion in 'Troubleshooting: Bugs, Questions and Support' started by Prime Overruler, Dec 29, 2024.

  1. Prime Overruler

    Prime Overruler
    Expand Collapse

    Joined:
    Nov 29, 2024
    Messages:
    7
    I'd like to create this mod:

    Code:
    {
    "x10_licenseplate_design_2_1": { "information": { "authors": "Timo Kinnunen", "name": "10x License Plate Designs", "value": 0 }
        ,"slotType": ["x10_licenseplate_design_2_1_deny", "licenseplate_design_2_1"]
        ,"slots2": [
            ["name", "allowTypes", "denyTypes", "default", "description"]
            ,["x10_licenseplate_design_2_1_0", ["licenseplate_design_2_1"], ["x10_licenseplate_design_2_1_deny"], "", "License Plate Design 1"]
            ,["x10_licenseplate_design_2_1_1", ["licenseplate_design_2_1"], ["x10_licenseplate_design_2_1_deny"], "", "License Plate Design 2"]
            ,["x10_licenseplate_design_2_1_2", ["licenseplate_design_2_1"], ["x10_licenseplate_design_2_1_deny"], "", "License Plate Design 3"]
            ,["x10_licenseplate_design_2_1_3", ["licenseplate_design_2_1"], ["x10_licenseplate_design_2_1_deny"], "", "License Plate Design 4"]
            ,["x10_licenseplate_design_2_1_4", ["licenseplate_design_2_1"], ["x10_licenseplate_design_2_1_deny"], "", "License Plate Design 5"]
            ,["x10_licenseplate_design_2_1_5", ["licenseplate_design_2_1"], ["x10_licenseplate_design_2_1_deny"], "", "License Plate Design 6"]
            ,["x10_licenseplate_design_2_1_6", ["licenseplate_design_2_1"], ["x10_licenseplate_design_2_1_deny"], "", "License Plate Design 7"]
            ,["x10_licenseplate_design_2_1_7", ["licenseplate_design_2_1"], ["x10_licenseplate_design_2_1_deny"], "", "License Plate Design 8"]
            ,["x10_licenseplate_design_2_1_8", ["licenseplate_design_2_1"], ["x10_licenseplate_design_2_1_deny"], "", "License Plate Design 9"]
            ,["x10_licenseplate_design_2_1_9", ["licenseplate_design_2_1"], ["x10_licenseplate_design_2_1_deny"], "", "License Plate Design 10"]
        ]}}
    The goal here is to allow several license plate slot mods to be used at the same time, whilst ensuring that this part, which also goes into the license plate slot, cannot be inserted inside itself (the slot2part mapping is not hierarchical and doesn't support this). I do this by giving the part a second slot type, a kind of "marker", that isn't used anywhere else except in this part to remove this part as an option for the slots that it provides.

    The documentation for the Slots2 (https://documentation.beamng.com/modding/vehicle/sections/slots/slots2/) argument denyTypes says:
    So, this should work; the part contains 2 slotTypes, 1 is allowed, 1 is denied, it should be denied.

    However, this is not checked for when constructing the Vehicle Config Parts tab UI, which leads to an infinite recursion and an eventual out of memory error:
    The function responsible for creating the problematic part of the UI is called _generateTreeBranch and it is located, for the new UI and the old UI, in \BeamNG.drive\ui\ui-vue\src\modules\vehicleConfig\parts\tree.js and \BeamNG.drive\ui\modules\vehicleconfig\vehicleconfig.js, respectively.

    I think the following additions and changes should work:
    Code:
    // UNCHANGED CODE START
    _generateTreeBranch(data, part, opts = { }, _depth = 0) {
        let res = []
        if (_depth > 200) return
        const defaultHighlight = !data.partsHighlighted
        for (const slotName in part.slotInfoUi) {
          const slotInfo = part.slotInfoUi[slotName]
          const chosenPart = data.chosenParts[slotName]
          let isHighlighted = defaultHighlight
          if (chosenPart === "") {
            isHighlighted = false
          } else if (data.partsHighlighted && chosenPart in data.partsHighlighted) {
            isHighlighted = data.partsHighlighted[chosenPart]
          }
          const element = {
            name: slotName,
            description: slotInfo.description,
            type: slotInfo.type, // slots1
            allowTypes: slotInfo.allowTypes, // slots2
            denyTypes: slotInfo.denyTypes, // slots2
            val: "",
            options: [],
            highlight: isHighlighted,
          }
          let hasOptions = false
          const slotAllowedTypes = slotInfo.allowTypes || [slotInfo.type]
    // UNCHANGED CODE END
    // NEW CODE START
          let slotDeniedPartNames = []
          for (let st of (slotInfo.denyTypes && Array.from(slotInfo.denyTypes) || [])) {
            if (!(st in data.slotMap)) continue
            for (const slotPartName of data.slotMap[st]) {
              if (slotPartName in data.availableParts) {
                slotDeniedPartNames.push(slotPartName)
              }
            }
          }
    // NEW CODE END
          for (const st of slotAllowedTypes) {
            if (!(st in data.slotMap)) continue
            for (const slotPartName of data.slotMap[st]) {
              if (slotPartName in data.availableParts) {
    // NEW CODE START
                if (slotPartName in slotDeniedPartNames) continue
    // NEW CODE END
                const slotPart = data.availableParts[slotPartName]
                const part = {
                  name: slotPartName,
                  description: slotPart.description,
                  isAuxiliary: slotPart.isAuxiliary,
                  val: slotPartName,
                }
                element.options.push(part)
                hasOptions = true
    // CHANGED CODE START
                if (chosenPart == slotPartName) {
    // CHANGED CODE WAS
               //if (data.chosenParts[slotName] === slotPartName || data.chosenParts[st] === slotPartName) {
    // CHANGED CODE END
                  element.val = slotPartName
                  if (slotPart.slotInfoUi) {
                    element.parts = this._generateTreeBranch(data, slotPart, opts, _depth + 1)
                  }
                  if ("parts" in element && (!Array.isArray(element.parts) || element.parts.length === 0)) {
                    delete element.parts
                  }
                }
              } else {
                console.error("slot part not found: ", slotPartName)
              }
            }
          }
          if (!("coreSlot" in slotInfo) && hasOptions) {
            element.options.unshift({
              name: "<empty>",
              description: emptyOptionName,
              val: "",
            })
          } else {
            element.open = true
          }
          if (!opts.simple || element.options.length > 2 || (element.options.length > 1 && element.options[0].val !== "") || _depth < 1) {
            element.options = formatOptions(element, opts)
            res.push(element)
          }
        }
        return res
      },
    
    The 1st change creates a list of denied parts from the list of denied types. This should be a NOP with all Slots usage and when denyTypes is empty with Slots2 usage.

    The 2nd change implements the check for denied types.

    The 3rd change prevents the first licenseplate_design_2_1 slot assignment from affecting the other subsequent licenseplate_design_2_1 assignments.

    I've included the changed files for your evaluation.
     

    Attached Files:

    • ui.zip

      File size:
      12.2 KB
      Views:
      86
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice