๐ŸŽฎ Train Sim World Controller Configuration Format

This document describes the structure and semantics of the configuration system used to map game controllers (e.g., joysticks, gamepads) to controls in Train Sim World using UE4SS. It is designed to be flexible, extensible, and friendly to both analog and digital input devices.


๐Ÿ“ฆ Overview

Each control on a game controller can be assigned an action. Assignments describe when and how the actions are triggered based on the input. Actions describe what happens when triggered.

All assignments conform to a top-level enum ControllerProfileControlAssignment, which contains the following variants:

Each assignment type has a specific use case and behavior, described below.


๐Ÿงฉ Assignment Types

๐Ÿ”˜ Momentary

Used for buttons that act while held.

{
  "type": "momentary",
  "threshold": 0.5,
  "action_activate": { ... },
  "action_deactivate": { ... }
}

๐Ÿ” Toggle

Used for toggle switches that alternate between two states.

{
  "type": "toggle",
  "threshold": 0.5,
  "action_activate": { ... },
  "action_deactivate": { ... }
}

๐Ÿ“ˆ Linear

Used for analog levers or sliders with multiple threshold points.

{
  "type": "linear",
  "thresholds": [
    { "threshold": 0.2, "action_activate": { ... }, "action_deactivate": { ... } },
    { "threshold": 0.7, "action_activate": { ... }, "action_deactivate": { ... } }
  ]
}

๐ŸŽš๏ธ DirectControl

Maps an analog controller input to a continuous value in-game.

{
  "type": "direct_control",
  "controls": "Throttle1",
  "input_value": {
    "min": 0.0,
    "max": 1.0,
    "invert": true
  },
  "notify": true
}

Options

| Name | Description | | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | hold | Whether to continuously hold this value. Useful for levers which automatically reset. (such as the Tube Deadman or some brake levers) | | notify | Whether to enable the in-game notifier when changing values to display the current value (defaults to true but can be explicitly disabled) |

๐Ÿงญ SyncControl

A safer alternative to DirectControl for unstable locos.

{
  "type": "sync_control",
  "identifier": "Reverser1",
  "input_value": {
    "min": -1.0,
    "max": 1.0,
    "steps": [-1.0, 0.0, 1.0]
  },
  "action_increase": { "keys": "PageUp" },
  "action_decrease": { "keys": "PageDown" }
}

๐ŸŽš๏ธ ApiControl

Maps an analog controller input to a continuous value in-game using the HTTP API.

{
  "type": "api_control",
  "controls": "Throttle1",
  "input_value": {
    "min": 0.0,
    "max": 1.0,
    "invert": true
  }
}

โš™๏ธ Action Types

Each assignment triggers an action when activated (and optionally when deactivated). Actions can be:

๐Ÿ–ฑ๏ธ Key Presses

{
  "keys": "W",
  "press_time": 0.1,
  "wait_time": 0.05
}

๐ŸŽ›๏ธ Direct Control Action

{
  "controls": "Throttle1",
  "value": 0.5,
  "hold": false,
  "relative": false
}

๐ŸŽ›๏ธ Api Control Action

{
  "controls": "Throttle1",
  "api_value": 0.5
}

๐Ÿ”ง Input Value Mapping

Used by DirectControl, SyncControl and ApiControl to map axis input to control values.

{
  "min": -1.0,
  "max": 1.0,
  "step": 0.1,
  "steps": [0.0, 0.2, null, 0.5, null, 1.0],
  "invert": true
}

๐Ÿ” Conditional assignments

It is also possible to only execute assignments depending on one or more conditions. This can be used to create multi-key assignments. (eg: the action of a button changes depending on the position of a lever). This can be added to any assignment using the conditions key:

{
  "type": "momentary",
  "conditions": [
    {
      "control": "mylever",
      "operator": "gte",
      "value": 0.5
    }
  ]
}

In the above example, the assignment will only execute if mylever exceeds 0.5. At this time the supported operators are gte, lte, gt and lt.


โœ… Best Practices


๐Ÿ“ Example Full Assignment

{
  "type": "momentary",
  "threshold": 0.5,
  "action_activate": {
    "keys": "H"
  },
  "action_deactivate": {
    "keys": "Shift+H"
  }
}

Happy simming! ๐Ÿš‚