Train plugin API (IRuntime)

This is the documentation for train plugins. In order to create a train plugin, implement the IRuntime interface from the OpenBveApi.Runtime namespace. In the following, you will find a description of how this interface works.

■ Contents
1. Overview
2. Function calls
3. Playing sounds
4. Supporting the AI


■ Overview

The following functions are called in this order when the plugin is loaded:

  Load
  SetVehicleSpecs
  Initialize
  SetPower
  SetBrake
  SetReverser
  SetSignal

The following functions can be called at any time:

  SetPower
  SetBrake
  SetReverser
  KeyDown
  KeyUp
  HornBlow
  DoorChange
  SetSignal
  SetBeacon
  PerformAI

The following function is called when the plugin is unloaded:

  Unload


● Function calls

The following is a list of all function calls along with explanations on their behavior.




bool Load(LoadProperties properties)

This function is the first to be called after the plugin has been loaded. When this function triggers inside the plugin, a matching call to Unload will be made when the plugin is unloaded.

Arguments:
LoadPropertiespropertiesThe properties supplied to the plugin on loading.

LoadProperties (class):
stringPluginFolderGets the absolute path to the plugin folder.
stringTrainFolderGets the absolute path to the train folder.
int[]PanelGets or sets the array of panel variables.
PlaySoundDelegatePlaySoundGets the callback function for playing sounds.
AISupportAISupportThe extent to which the plugin supports the AI.
stringFailureReasonGets or sets the reason why the plugin failed loading.

PlaySound (function):
See the section on
playing sounds.

AISupport (enumeration):
AISupport.None0The plugin does not support the AI. Calls to PerformAI will not be made. Non-player trains will not use the plugin.
AISupport.Basic1The plugin complements the built-in AI by performing only functions specific to the plugin.

Return value:
boolWhether the plugin was loaded successfully.

If your plugin uses external configuration files, open files relative to the properties.PluginFolder or properties.TrainFolder. The difference between the two can be best visualized when the plugin is in a shared folder: Files relative to the plugin folder are common for all trains that use the plugin, while files relative to the train folder are specific to each train. The plugin folder is simply the folder in which the plugin is stored.

Initialize the properties.Panel array to any size and with any startup values you need. The array is processed after every call to Elapse by the host application in order to update custom panel indicators. Developers of the panel2.cfg file can query these values with the atsi variable, while developers of the panel.animated file can query these values with the pluginState[i] variable.

The properties.PlaySound function can be used at any time to play sounds. This callback function returns a handle that you can use to track if the sound is still playing, to change its volume and pitch, or to stop it. Please also see the section on playing sounds.

Set properties.AISupport to a value other than AISupport.None if you want your plugin to support the AI. Please also see the section on supporting the AI.

You should return true as the return value when loading succeeded. If, on the other hand, you want to abort loading the plugin, for example because an external configuration file could not be found, return false and set properties.FailureReason to a human-readible string that explains why loading failed.




void Unload()

This function is the last to be called before the plugin is unloaded.




void SetVehicleSpecs(VehicleSpecs specs)

This function is called after Load to inform the plugin about the specifications of the train.

Arguments:
VehicleSpecsspecsThe specifications of the train.

VehicleSpecs (class):
intPowerNotchesGets the number of power notches the train has.
BrakeTypesBrakeTypeGets the type of brake the train uses.
intBrakeNotchesGets the number of brake notches the train has, including the hold brake, but excluding the emergency brake.
boolHasHoldBrakeGets whether the train has a hold brake.
intAtsNotchGets the index of the brake notch that corresponds to B1 or LAP.
intB67NotchGets the index of the brake notch that corresponds to 70% of the available brake notches.
intCarsGets the number of cars the train has.

BrakeTypes (enumeration):
BrakeTypes.ElectromagneticStraightAirBrake0The train uses the electromagnetic straight air brake.
BrakeTypes.ElectricCommandBrake1The train uses the analog/digital electro-pneumatic air brake without a brake pipe (electric command brake).
BrakeTypes.AutomaticAirBrake2The train uses the automatic air brake with partial release.

For more information about the meanings of the notches, see the sections on SetReverser, SetPower and SetBrake.




void Initialize(InitializationModes mode)

This function is called after SetVehicleSpecs and informs the plugin about the mode the safety system should start in. If the safety system in your plugin can be activated or deactivated, you should initialize the state of the plugin accordingly. When the user selects a Jump to station target, this function is also called prior to moving the train to its new location.

Arguments:
InitializationModesmodeThe mode of initialization.

InitializationModes (enumeration):
InitializationModes.OnService-1The safety system should be enabled. The train has its service brakes applied.
InitializationModes.OnEmergency0The safety system should be enabled. The train has its emergency brakes applied.
InitializationModes.OffEmergency1The safety system should be disabled. The train has its emergency brakes applied.

The initialization mode is set in CSV/RW routes via the Route.Change command. Please note that any value between -2147483648 and 2147483647 can be conveyed to the plugin - the enumeration members are simply meant to standardize the meanings of the initialization modes.




void Elapse(ElapseData data)

This function is called every frame. It informs the plugin about the current state of the train and allows to set the virtual handles.

Arguments:
ElapseDatadataThe data passed to the plugin.

ElapseData (class):
VehicleStateVehicleGets the state of the train.
PrecedingVehicleStatePrecedingVehicleGets the state of the preceding train, or a null reference if there is no preceding train.
HandlesHandlesGets or sets the virtual handles.
TimeTotalTimeGets the absolute in-game time.
TimeElapsedTimeGets the time that elapsed since the last call to Elapse.
stringDebugMessageGets or sets the debug message the plugin wants the host application to display.

VehicleState (class):
doubleLocationGets the location of the front of the train, in meters.
SpeedSpeedGets the speed of the train.
doubleBcPressureGets the pressure in the brake cylinder, in pascal.
doubleMrPressureGets the pressure in the main reservoir, in pascal.
doubleErPressureGets the pressure in the equilizing reservoir, in pascal.
doubleBpPressureGets the pressure in the brake pipe, in pascal.
doubleSapPressureGets the pressure in the straight air pipe, in pascal.

PrecedingVehicleState (class):
doubleLocationGets the location of the back of the preceding train, in meters.
doubleDistanceGets the distance from the front of the current train to the back of the preceding train, in meters.
SpeedSpeedGets the speed of the preceding train.

Speed (structure):
doubleMetersPerSecondGets the speed in meters per second.
doubleKilometersPerHourGets the speed in kilometers per hour.
doubleMilesPerHourGets the speed in miles per hour.

Time (structure):
doubleSecondsGets the time in seconds.
doubleMillisecondsGets the time in milliseconds.

Handles (class):
intReverserGets or sets the reverser position.
intPowerNotchGets or sets the power notch.
intBrakeNotchGets or sets the brake notch.
boolConstSpeedGets or sets whether the const speed system is enabled.

The meanings of the notches are explained in the sections on SetReverser, SetPower and SetBrake.




void SetReverser(int reverser)

This function is called when the driver changes the reverser position.

Arguments:
intreverserThe new reverser position.

For reverser, the value of -1 corresponds to backward, 0 to neutral and 1 to forward.




void SetPower(int powerNotch)

This function is called when the driver changes the power notch.

Arguments:
intpowerNotchThe new power notch.

For powerNotch, the value passed can range from 0 to specs.PowerNotches.




void SetBrake(int brakeNotch)

This function is called when the driver changes the brake notch.

Arguments:
intbrakeNotchThe new brake notch.

For trains with the automatic air brake, 0 is RELEASE, 1 is LAP, 2 is SERVICE and 3 is EMERGENCY.

For all other trains without a hold brake, 0 is released brakes, 1 is brake notch B1, 2 is brake notch B2, etc., specs.BrakeNotches is the maximum brake notch, and specs.BrakeNotches+1 is the emergency brake.

For all other trains with a hold brake, 0 is released brakes, 1 is the hold brake, 2 is brake notch B1, 3 is brake notch B2, etc., specs.BrakeNotches is the maximum brake notch, and specs.BrakeNotches+1 is the emergency brake.

Generally speaking, for trains without the automatic air brake, specs.AtsNotch is brake notch B1 and specs.BrakeNotches is the maximum service brake notch. For all types of trains, specs.BrakeNotches+1 is the emergency brake.




void KeyDown(VirtualKeys key)

This function is called when a plugin-specific key is pressed.

Arguments:
VirtualKeyskeyThe virtual key that was pressed.

VirtualKeys (enumeration):
VirtualKeys.S0The virtual S key. The default assignment is Space.
VirtualKeys.A11The virtual A1 key. The default assignment is Insert.
VirtualKeys.A22The virtual A2 key. The default assignment is Delete.
VirtualKeys.B13The virtual B1 key. The default assignment is Home.
VirtualKeys.B24The virtual B2 key. The default assignment is End.
VirtualKeys.C15The virtual C1 key. The default assignment is Page Up.
VirtualKeys.C26The virtual C2 key. The default assignment is Page Down.
VirtualKeys.D7The virtual D key. The default assignment is 2.
VirtualKeys.E8The virtual E key. The default assignment is 3.
VirtualKeys.F9The virtual F key. The default assignment is 4.
VirtualKeys.G10The virtual G key. The default assignment is 5.
VirtualKeys.H11The virtual H key. The default assignment is 6.
VirtualKeys.I12The virtual I key. The default assignment is 7.
VirtualKeys.J13The virtual J key. The default assignment is 8.
VirtualKeys.K14The virtual K key. The default assignment is 9.
VirtualKeys.L15The virtual L key. The default assignment is 0.

When making use of plugin-specific keys in your plugin, be sure to release a documentation that includes the virtual names of the keys along with their plugin-specific meanings.




void KeyUp(VirtualKeys key)

This function is called when a plugin-specific key is released.

Arguments:
VirtualKeyskeyThe virtual key that was released.

VirtualKeys (enumeration):
VirtualKeys.S0The virtual S key. The default assignment is Space.
VirtualKeys.A11The virtual A1 key. The default assignment is Insert.
VirtualKeys.A22The virtual A2 key. The default assignment is Delete.
VirtualKeys.B13The virtual B1 key. The default assignment is Home.
VirtualKeys.B24The virtual B2 key. The default assignment is End.
VirtualKeys.C15The virtual C1 key. The default assignment is Page Up.
VirtualKeys.C26The virtual C2 key. The default assignment is Page Down.
VirtualKeys.D7The virtual D key. The default assignment is 2.
VirtualKeys.E8The virtual E key. The default assignment is 3.
VirtualKeys.F9The virtual F key. The default assignment is 4.
VirtualKeys.G10The virtual G key. The default assignment is 5.
VirtualKeys.H11The virtual H key. The default assignment is 6.
VirtualKeys.I12The virtual I key. The default assignment is 7.
VirtualKeys.J13The virtual J key. The default assignment is 8.
VirtualKeys.K14The virtual K key. The default assignment is 9.
VirtualKeys.L15The virtual L key. The default assignment is 0.

When making use of plugin-specific keys in your plugin, be sure to release a documentation that includes the virtual names of the keys along with their plugin-specific meanings.




void HornBlow(HornTypes type)

This function is called when a horn starts playing. In case of the musical horn, this function is also called when the horn stops playing.

Arguments:
HornTypestypeThe type of horn.

HornTypes (enumeration):
HornTypes.Primary0The primary horn.
HornTypes.Secondary1The secondary horn.
HornTypes.Music2The musical horn.




void DoorChange(DoorStates oldState, DoorStates newState)

This function is called when the state of the doors change.

Arguments:
DoorStatesoldStateThe old state of the doors.
DoorStatesnewStateThe new state of the doors.

DoorStates (enumeration):
DoorStates.None0No door is open.
DoorStates.Left1The left doors are open.
DoorStates.Right2The right doors are open.
DoorStates.Both3All doors are open.




void SetSignal(SignalData[] data)

This function is called when the aspect in the current or in any of the upcoming section changes, or when passing section boundaries. For the current section, it is assumed that no train is currently inside. Only sections until the first red section are reported.

Arguments:
SignalData[]dataThe signal data per section.

SignalData (class):
intAspectGets the aspect of the section.
doubleDistanceGets the distance to the section.

The data array contains one entry per section, where data[0] is the current section, data[1] the upcoming section, and so on. You can inspect the aspect and the distance to each section reported.

Please note that the length of the data array is dynamic. Only sections until the first red section are reported. This means that you need to check the size of the array before querying a particular section.

Please also note that the last section in the data array does not have to be red necessarily. For example at the end of the track, the last section might be green.

In CSV/RW routes, the Track.Section (CSV) or @Section (RW) command is used to create signalling sections. The data.Aspect member corresponds to any of the aspects defined by this command.




void SetBeacon(BeaconData data)

This function is called when a beacon is passed by the front of the train.

Arguments:
BeaconDatadataThe beacon data.

BeaconData (class):
intTypeGets the type of beacon.
intOptionalGets optional data the beacon transmits.
SignalDataSignalGets the section the beacon is attached to.

SignalData (class):
intAspectGets the aspect of the section.
doubleDistanceGets the distance to the section.

In CSV/RW routes, the Track.Beacon (CSV) or @Beacon (RW) command is used to install beacons on the route. Both the beacon type and the optional data set by this command is transmitted to the train plugin along with the distance to the attached section.

Please note that plugins may receive beacon types less than 0. These beacon types are reserved for future use and must be ignored by current plugins.




void PerformAI(AIData data)

This function is called when the AI is performed.

Arguments:
AIDatadataThe AI data.

AIData (class):
HandlesHandlesGets or sets the driver handles.
AIResponseResponseGets or sets the AI response.

Whenever you let the AI perform something, set data.Response to a value other than AIResponse.None. Please also see the section on supporting the AI.


■ Playing sounds

You can play custom sounds from within your plugin. Custom sounds need to be configured inside the sound.cfg file before they can be used by the plugin. In order to play such sounds, keep a reference to the PlaySound function that is passed in the Load call. You can then start playing sounds at any time by calling this function:

SoundHandle PlaySound(int index, double volume, double pitch, bool looped)

Arguments:
intindexThe index to the sound to be played.
doublevolumeThe initial volume of the sound. A value of 1.0 represents nominal volume.
doublepitchThe initial pitch of the sound. A value of 1.0 represents nominal pitch.
boolloopedWhether the sound should be played in an indefinate loop.

Return value:
SoundHandleThe handle to the sound, or a null reference if the sound could not be played.

When you call the PlaySound function, a handle will be returned that you can use to later check if the sound is still playing, in order to change the volume or pitch, or to stop playing the sound. If you play a sound in a loop, you must keep the handle in order to subsequently stop the sound - otherwise it would play indefinately. The handle returned by PlaySound is of the following form:

SoundHandle (class):
boolPlayingGets whether the sound is still playing. Once this returns false, the sound handle is invalid.
boolStoppedGets whether the sound has stopped. Once this returns true, the sound handle is invalid.
doubleVolumeGets or sets the volume. A value of 1.0 represents nominal volume.
doublePitchGets or sets the pitch. A value of 1.0 represents nominal pitch.
voidStop()Stops the sound and invalidates the handle.

Please note that depending on the implementation by the host application, sounds that are not in audible range may not be played at all.

Please also note that the handle returned might be a null reference in the case the host application could not play the sound, for example because the file could not be found.


■ Supporting the AI

Usually, the host application performs the AI. However, your plugin might require special operation precedures which the built-in AI cannot know of. For this reason, you can complement the built-in AI by performing operation procedures specific to your plugin. Before considering to support the AI, however, you should understand what the AI is intended to represent: a human being standing or sitting in the cab, operating levers and pressing buttons, just like the player. This means that the AI must not operate 6 levers and 12 buttons simultaneously, but only do one thing at a time.

If you want to support the AI, first set data.AISupport inside the Load call to AISupport.Basic. Whenever the host application performs an AI round, a call to PerformAI will then be made inside the plugin. The plugin can then decide to let the AI perform an action, or to pass and let the host application perform an action. Different kinds of actions can take different amounts of time, so whenever the plugin lets the AI perform an action, it will also set the time it takes before the next action can be performed.

It is important to understand that unless your plugin also simulates a full ATO with automatic stopping at stations, you must let the host application perform the AI for most of the time and only intervene if absolutely necessary, for example in order to start the engine, to acknowledge a vigilance device, etc. Whenever this is the case, react to the PerformAI call appropriately:

void PerformAI(AIData data)

Arguments:
AIDatadataThe AI data.

AIData (class):
HandlesHandlesGets or sets the driver handles.
AIResponseResponseGets or sets the AI response.

Handles (class):
intReverserGets or sets the reverser position.
intPowerNotchGets or sets the power notch.
intBrakeNotchGets or sets the brake notch.
boolConstSpeedGets or sets whether the const speed system is enabled.

The meanings of the notches are explained in the sections on SetReverser, SetPower and SetBrake.

AIResponse (enumeration):
AIResponse.NoneNo action was performed by the plugin.
AIResponse.ShortThe action performed took a short time.
AIResponse.MediumThe action performed took an average amount of time.
AIResponse.LongThe action performed took a long time.

You can directly control the driver handles with the data.Handles member, for example if you want to cut power or apply a certain brake notch. For plugin-specific actions, you should only simulate key presses, for example by calling KeyDown or KeyUp. This will prevent you from letting the AI cheat in any way. If you let the AI operate the handles, you should only change by one notch at a time with a short response time.

If you decide to let the AI do something, you must set the data.Response member to a meaningful value. For operating the handles, best use a short response time, while for other actions like turning a switch not directly accessible, use a long response time. Note that the actual timings are at the whim of the host application.

Example:
if (AtsAlarm) {
    /* The driver needs to cut power and apply the brakes,
     * then press the virtual S key.*/
    if (data.Handles.PowerNotch > 0) {
        /* We change only by one notch at a time. */
        data.Handles.PowerNotch -= 1;
        data.Response = AIResponse.Short;
    } else if (data.Handles.BrakeNotch < 2) {
        /* We change only by one notch at a time. */
        data.Handles.BrakeNotch += 1;
        data.Response = AIResponse.Short;
    } else {
        /* We simulate a key press here. */
        KeyDown(VirtualKeys.S);
        data.Response = AIResponse.Medium;
    }
} else if (AtoActive) {
    /* Our ATO does not require driver interaction, so
     * let's prevent the built-in AI from doing anything. */
    data.Response = AIResponse.Long;
} else {
    /* Let the host application perform a default action
     * such as braking for signals or stations. */
    data.Response = AIResponse.None;
}