TNI Socket Interface

From TrainzOnline
(Difference between revisions)
Jump to: navigation, search
(TNISocket Library Calls)
(Overview)
Line 9: Line 9:
  
 
==Overview==
 
==Overview==
TNISocket is a TNILibrary built into Trainz native code. It is entirely thread safe, and may be loaded and called at any time from within any TNI plugin DLL.
+
TNISocket is a TNILibrary built into Trainz native code. It is entirely thread safe, and may be loaded and called at any time from within any TNI plugin DLL, using the TNIGetContextLibrary function.
  
 
TNISocket is intended to be used for the following purposes:
 
TNISocket is intended to be used for the following purposes:
Line 29: Line 29:
  
 
Text-based protocols can be implemented by building the desired string and sending it via TNIStreamWriteInt8Array(), and by reading character-at-a-time using TNIStreamReadInt8() until encountering the appropriate CR/LF sequence.
 
Text-based protocols can be implemented by building the desired string and sending it via TNIStreamWriteInt8Array(), and by reading character-at-a-time using TNIStreamReadInt8() until encountering the appropriate CR/LF sequence.
 
  
 
==TNISocket Library Calls==
 
==TNISocket Library Calls==

Revision as of 10:21, 12 December 2017

TNISocket is a builtin TNILibrary within Trainz which allows TNI plugins to communicate via TCP/IP sockets.

Before continuing, it is recommended you first familiarise yourself with the following pages:

NOTE: This page is a work in progress, and reflects technologies which are not yet present in retail versions of Trainz.


Contents

Overview

TNISocket is a TNILibrary built into Trainz native code. It is entirely thread safe, and may be loaded and called at any time from within any TNI plugin DLL, using the TNIGetContextLibrary function.

TNISocket is intended to be used for the following purposes:

  • Communication with networked hardware devices running on the same local network as the Trainz client. Examples include dedicated train-related hardware such as DCC interfaces or cab simulators, or kit-form hardware such as raw electronics interfaces. Devices such as printers, and standard services such as file shares or web sites should not be accessed in this manner.
  • Communication with executable processes running on the same machine, or same local network, as the Trainz client. The executable processes should remain usable while Trainz is not running, and Trainz should remain usable while the executable process is not running. This means that an external process which helps efficiently edit a route or session is permitted, but an external process which is required to load or drive a given route or session is not permitted.

The following usages are specifically prohibited:

  • Communication between multiple Trainz clients for purposes such as multiplayer chat, gameplay, etc. (You should use the iTrainz network for this purpose.)
  • Exposing the TNI to an external process for the primary purpose of avoiding the security and code-review limitations placed on the TNI plugins. In short, if your desired goal can be achieved without relying on an external process, we will reject any TNI proposal for an external process implementation.
  • File sharing, web browsing, etc. Use a web browser, there's no need to build this into Trainz.
  • Generic socket access. Your TNI proposal must clearly indicate how your plugin will use the TNISocket interface, and you must take reasonable precautions to avoid using the TNISocket interface outside of that specified purpose.
  • Excessive activity. Your plugin should perform a reasonable minimum of effort to achieve its goals. Plugins which generate substantial amounts of network traffic without showing a clear requirement will be rejected. This includes metrics such as bytes per second, packets per second, and connections per second.

TNISocket communication is implemented through TNIStream. Certain stream operations, such as TNIStreamTell(), TNIStreamGetSize(), and TNIStreamStoreObjectReference(), are not available on socket streams. All TNIStream operations are blocking, and are interrupted by stream closure.

As TNISocket is a blocking interface, any call made will block the calling thread until it completes or fails. Care must be taken to ensure that your TNI Plugin does not become unresponsive - this will not affect the Trainz client but may affect your plugin's user experience and may be cause for rejection of your plugin. The simplest approach is to spawn a new thread per socket, ensuring that delays on a socket do not lead to delays on your TNI Plugin's librarycall interface. Since most TNI plugins will only work with one socket, this is not expected to cause any difficulties or performance concerns.

All data is encoded as raw binary with no additional protocol. Text strings are sent as a 32-bit length followed by raw UTF8. AssetIDs do not yet have a standardised format and should not be used. Raw data bytes may be sent using TNIStreamWriteInt8Array().

Text-based protocols can be implemented by building the desired string and sending it via TNIStreamWriteInt8Array(), and by reading character-at-a-time using TNIStreamReadInt8() until encountering the appropriate CR/LF sequence.

TNISocket Library Calls

OpenRemote

Open a new socket. This function blocks until the socket connects, the connection attempt fails, or a timeout occurs. It is not possible for the plugin to cancel a connection attempt.

Parameters - a 4 element TNIArray* containing:

  • A TNIString* containing an index into the socket templates in the config file
  • A TNIString* containing the IPv4 address or domain name to connect to
  • A TNIString* containing the TCP port number to connect to
  • A TNILibrary* to the calling library

Return value - A 2 element TNIArray* containing:

  • A TNIString* containing an empty string on success, or an error label on failure
  • A TNIStream* for the newly connected stream on success, or nullptr on failure. The stream is closed when this object is fully released.

Listen

Binds a local TCP port to listen for incoming connections. When an incoming connection occurs, a library call is sent to the observer with the new TNIStream* as its parameter. If this call is ignored, the incoming connection is immediately dropped- this will be seen by the remote end as an connection and immediate disconnection, not as a connection failure.

Parameters - a 4 element TNIArray* containing:

  • A TNIString* containing an index into the socket templates in the config file
  • A TNILibrary* to the calling library

Return value - A 2 element TNIArray* containing:

  • A TNIString* containing an empty string on success, or an error label on failure
  • TNIObject* - An object representing this listen port. The listen port is closed when this object is fully released.

Close

Closes a socket. This prevents any further send or receive activity on the socket, and will cancel any outstanding send or receive calls in progress on another thread. The remote endpoint can typically detect closure. Calling this function multiple times is harmless but pointless. Fully releasing the socket object implies automatic closure.

Parameters:

  • A TNIStream* for the socket to close

Return value:

  • None

Poll

Asynchronously queries a socket's status. A single library call is made to the observer when either:

  • The requested amount of unread data is present on the socket
  • An error is flagged on the socket.

The library call may happen at any time after the Poll function is called, including immediately if the necessary conditions are already met. To receive additional notifications past the first, another call to Poll must be placed.

It is recommended that only a single call to Poll is placed on a given socket at a time. If multiple calls are made to Poll on a single socket before the notification is placed, only the last is honored. This can be problematic however, as there is no way for the caller to know which (or how many) of the Poll calls were honored. The cookie may be used to distinguish between Poll calls in this scenario, however the returned bytecount may be misleading since the notifications may overlap in time.

Parameters - a 4 element TNIArray* containing:

  • A TNIStream* for the socket to poll
  • A TNIString* containing the number of bytes required to trigger a notification
  • A TNILibrary* to the calling library
  • A TNIObject* which is not used by the Poll function itself, but will be passed to the observer notification function when the poll conditions are met

Return value:

  • None

NotifyConnection

Notifies an observer of a new connection after a previous call to the Listen function. If this call is ignored, the incoming connection is immediately dropped- this will be seen by the remote end as an connection and immediate disconnection, not as a connection failure.

Parameters:

  • A TNIStream* for the newly opened connection

Expected return value:

  • None

NotifyPoll

Notifies an observer that the requested conditions have been met for a previous call to Poll. This will be called once per call to Poll, to receive additional notifications another call to Poll must be placed.

Parameters:

  • The TNIObject* passed to the Poll function

Expected return value:

  • None
Personal tools