TNI TrainzScript Interface

From TrainzOnline
Jump to: navigation, search

The TNI TrainzScript Interface is a protocol and associated functions which allows communication between TrainzScript and a third-party TNI plugin.

All TNI plugin DLLs must adhere to the TNI Core Interface.

This page is a work in progress, and reflects technologies which are not present in the retail version of Trainz.


Contents

Creating a TNI Library Asset

In order to access a TrainzNativeInterface plugin from TrainzScript, the following steps are taken:

  • The TNI plugin DLL is created in the normal fashion. (eg. "ScriptedPlugin.dll")
  • A single KIND Library asset is created to represent the TNI plugin. (eg. "ScriptedPluginLibrary")
  • The "tni-library-name" tag is added to the library asset, with the value being the name of the TNI plugin DLL. (eg. "tni-library-name ScriptedPlugin")


Accessing a TNI Library from TrainzScript

Accessing a TNI Library from TrainzScript is fairly straightforward:

  • The library ("ScriptedPluginLibrary") is loaded by script in the normal fashion, using TrainzScriptBase.GetLibrary(). This causes the native TNI plugin DLL ("ScriptedPlugin.dll") to be loaded, and an instance of the plugin's TNILibrary to be created for the current script context.
  • Any calls to Library.LibraryCall() on the library object which are not intercepted by script are forwarded to the native TNILibrary dispatch function. The 'function' and 'stringParam' arguments to the LibraryCall() function are passed as the 'functionName' and 'params' arguments, respectively. The 'objectParam' parameter is reserved for future expansion and should be left null or empty.
  • LibraryCalls made from TrainzScript threads will cause the thread to sleep while the TNI plugin responds to the function call. This will take a minimum of one render frame, and so rapid dispatch of many functions in this manner is not practical.
  • LibraryCalls made from non-threaded code will cause the TNI plugin to respond to the function call in the background, and will immediately return an empty string to the calling script.
  • Regardless of which approach is used, all calls are placed in a queue and are executed in-order on a single native thread. If the script context is shut down while function calls are outstanding, those function calls may be cancelled.


Accessing TrainzScript from a TNI plugin

TNI plugins cannot call directly into TrainzScript, however they can post messages to their Library object. The Library's script, or other scripts which Sniff() the Library, can receive the messages and process them as required.

The plugin can specify the Major and Minor message strings, as well as a Soup payload that may contain value data types (strings, bools, KUIDs, etc.) and additional Soup objects. As with all TrainzScript message processing, the message queue is of a limited capacity, and if the plugin is adding messages to the queue faster than the script is processing them, a message queue overflow will eventually occur and data will be lost.

TBD: detail the functions required for this technique.


Interoperability

It is recommended that any TNI Library asset which is designed to interface with TrainzScript is designed for maximum reuse. The Library asset's script should perform the job of interfacing with the plugin, and should expose a stable, simple and future-proof interface to other scripts. Where feasible, such libraries should function independently of other systems created by the same author, allowing individual components to be replaced by other developers or end-users without having to replace every component including the TNI plugin itself. Library public interfaces should be clearly documented including details on expected usage, legal parameter ranges, error handling, etc.

For example, where a given TNI developer is building a physical Train Controller unit and providing a corresponding in-game HUD, the following structure is appropriate:

  • The TNI Plugin, which interacts with the hardware. This may interact with Trainz directly for the basic control mechanisms, but may offer additional non-standard functionality to script.
  • The Library asset corresponding to the TNI Plugin. This communicates with the TNI Plugin, coordinating the sending of data and settings to the plugin, and the receiving of control input changes from the plugin. It requires the TNI Plugin to operate, but does not require or reference any other script libraries.
  • A separate HUD Library. This utilises the TNI Plugin's Library to display control state information. It requires the Library asset, and displays a generic failure message in the HUD if the Library cannot be loaded or if the hardware is not present.
  • A scripted Interior asset, which utilises the TNI Plugin's Library to reflect the state of some of the custom controls into the virtual cabin. It continues to work if the Library cannot be loaded or if the hardware is not present, but obviously loses the custom control functionality.

Breaking up the various components as described here offers the following advantages:

  • If the user unplugs the Physical Controller, the scripted HUD and the train Interior do not fail.
  • If the user uninstalls the TNI Plugin, the train continues to work.
  • If a third party wishes to make their own train interior which is compatible with the Physical Controller, they can do so.
  • If a third party wishes to integrate some of the functionality into their own HUD, they can do so.
  • If an end user wishes to use the Physical Controller, but does not wish to use the HUD, they can do so.
  • If a new version of Trainz is released, where the HUD is for some reason no longer appropriate, the HUD can be disabled or replaced without having to redevelop the TNI plugin or the Interior assets.
Personal tools