TNI Core Interface
(Created page with "The TNI Core Interface is a protocol and associated functions which allows communication between Trainz and a third-party TNI plugin. =DLL Devel...") |
|||
Line 29: | Line 29: | ||
The following TNIObject-derived classes are provided by the [[TNI Core Interface]]: | The following TNIObject-derived classes are provided by the [[TNI Core Interface]]: | ||
+ | |||
+ | ==TNIObject== | ||
+ | TNIObject is the polymorphic base class for all the entire TNI object hierarchy. All TNIObjects are considered opaque types, are constructed via helper functions provided in the TrainzNativeInterface DLL, and are released with TNIRelease(). | ||
+ | |||
+ | <code><pre> | ||
+ | // ============================================================================ | ||
+ | // TNI OBJECT TYPES | ||
+ | // ============================================================================ | ||
+ | enum | ||
+ | { | ||
+ | TNI_CLASS_NONE = 0, | ||
+ | TNI_CLASS_OBJECT = 1, | ||
+ | TNI_CLASS_STRING = 2, | ||
+ | TNI_CLASS_ARRAY = 3, | ||
+ | TNI_CLASS_CONTEXT = 4, | ||
+ | TNI_CLASS_LIBRARY = 5, | ||
+ | TNI_CLASS_STREAM = 6, | ||
+ | TNI_CLASS_GRAPHICS = 7, | ||
+ | TNI_CLASS_VECTOR = 8, | ||
+ | TNI_CLASS_ASSETID = 9, | ||
+ | TNI_CLASS_LABEL = 10, | ||
+ | TNI_CLASS_BUFFER = 11, | ||
+ | TNI_CLASS_SOUP = 12, | ||
+ | }; | ||
+ | |||
+ | |||
+ | // ============================================================================ | ||
+ | // Name: TNIGetObjectClass | ||
+ | // Desc: Return the type of the specified object, as per the above enumeration | ||
+ | // values. | ||
+ | // Parm: object - the object to query. It is safe to pass a NULL value here. | ||
+ | // ============================================================================ | ||
+ | uint32_t TNIGetObjectClass(const TNIObject* object); | ||
+ | |||
+ | |||
+ | // ============================================================================ | ||
+ | // Name: TNIReference | ||
+ | // Desc: Add a reference to the specified object. The object will be released | ||
+ | // when the reference count drops to zero. | ||
+ | // Parm: object - The object to which we will add a reference. It is safe to | ||
+ | // pass a NULL value here. | ||
+ | // ============================================================================ | ||
+ | void TNIReference(const TNIObject* object); | ||
+ | |||
+ | |||
+ | // ============================================================================ | ||
+ | // Name: TNIRelease | ||
+ | // Desc: Remove a reference to the specified object. The object will be | ||
+ | // released when the reference count drops to zero. TNIRelease() should | ||
+ | // be called exactly once for each call to TNIReference() and once for | ||
+ | // any function return value (for example, from TNIAllocString().) | ||
+ | // Parm: object - The object from which we will remove a reference. It is safe | ||
+ | // to pass a NULL value here. | ||
+ | // ============================================================================ | ||
+ | void TNIRelease(const TNIObject* object); | ||
+ | </pre></code> | ||
+ | |||
+ | |||
==TNIString== | ==TNIString== | ||
TNIString provides a simple string class. The raw string data is zero-terminated in the usual C fashion, and is encoded as UTF-8. UTF-8 is a superset of ASCII, so ASCII strings can be stored directly in a TNIString with no conversion necessary. | TNIString provides a simple string class. The raw string data is zero-terminated in the usual C fashion, and is encoded as UTF-8. UTF-8 is a superset of ASCII, so ASCII strings can be stored directly in a TNIString with no conversion necessary. | ||
+ | |||
+ | <code><pre> | ||
+ | // ============================================================================ | ||
+ | // Name: TNIAllocString | ||
+ | // Desc: Allocates a TNIString given a zero-terminated UTF8 text buffer. | ||
+ | // Parm: utf8text - The UTF-8 text buffer. The contents of the buffer are | ||
+ | // copied into the TNIString. The function results are undefined if this | ||
+ | // parameter does not represent a valid UTF-8 encoded string. | ||
+ | // Retn: TNIString - The newly allocated TNIString. | ||
+ | // Note: You must eventually release the allocated string using TNIRelease(). | ||
+ | // Note: If 'utf8text' is nullptr, the returned TNIString is an empty string. | ||
+ | // ============================================================================ | ||
+ | TNIString* TNIAllocString(const char* utf8text); | ||
+ | |||
+ | |||
+ | // ============================================================================ | ||
+ | // Name: TNIGetStringText | ||
+ | // Desc: Retrieves a pointer to the internal text buffer of the TNIString | ||
+ | // object. This is a read-only reference, and remains valid only while | ||
+ | // (1) you hold a reference to the TNIString, and (2) you do not modify | ||
+ | // the TNIString. | ||
+ | // Parm: string - The TNIString object to query. | ||
+ | // Retn: const char* - A temporary pointer to the internal string text, in | ||
+ | // utf8 encoding. | ||
+ | // Note: If 'string' is a nullptr, the return value is a pointer to an empty | ||
+ | // string. | ||
+ | // ============================================================================ | ||
+ | const char* TNIGetStringText(const TNIString* string); | ||
+ | |||
+ | |||
+ | // ============================================================================ | ||
+ | // Name: TNIGetStringSize | ||
+ | // Desc: Returns the size in bytes of the specified string. | ||
+ | // Parm: string - A TNIString object, or nullptr. | ||
+ | // Retn: size_t - The size in bytes of the text string. Note that this is the | ||
+ | // size of the UTF-8 encoded text data, not including zero termination, | ||
+ | // and not including any overheads. If the string is null or empty, this | ||
+ | // returns zero. | ||
+ | // ============================================================================ | ||
+ | size_t TNIGetStringSize(const TNIString* string); | ||
+ | |||
+ | |||
+ | // ============================================================================ | ||
+ | // Name: TNISetStringText | ||
+ | // Desc: Modifies the TNIString to a copy of the supplied UTF-8 encoded text | ||
+ | // buffer. | ||
+ | // Parm: string - The existing TNIString object to modify. Modifying the string | ||
+ | // invalidates any pointer previously returned by TNIGetStringText(). | ||
+ | // Parm: utf8text - The UTF-8 text buffer. The contents of the buffer are copied | ||
+ | // into the TNIString. The function results are undefined if this | ||
+ | // parameter does not represent a valid UTF-8 encoded string. | ||
+ | // Note: If 'string' is a nullptr, this function does nothing. | ||
+ | // Note: If 'utf8text' is a nullptr, it is considered an empty string. | ||
+ | // ============================================================================ | ||
+ | void TNISetStringText(TNIString* string, const char* utf8text); | ||
+ | |||
+ | |||
+ | // ============================================================================ | ||
+ | // Name: TNIAppendStringText | ||
+ | // Desc: Appends the supplied UTF-8 encoded text buffer to the TNIString. | ||
+ | // Parm: string - The existing TNIString object to modify. Modifying the string | ||
+ | // invalidates any pointer previously returned by TNIGetStringText(). | ||
+ | // Parm: utf8text - The UTF-8 text buffer. The contents of the buffer are copied | ||
+ | // into the TNIString. The function results are undefined if this | ||
+ | // parameter does not represent a valid UTF-8 encoded string. | ||
+ | // Note: If 'string' is a nullptr, this function does nothing. | ||
+ | // Note: If 'utf8text' is a nullptr, this function does nothing. | ||
+ | // ============================================================================ | ||
+ | void TNIAppendStringText(TNIString* string, const char* utf8text); | ||
+ | </pre></code> | ||
+ | |||
==TNILabel== | ==TNILabel== |
Revision as of 14:54, 11 May 2016
The TNI Core Interface is a protocol and associated functions which allows communication between Trainz and a third-party TNI plugin.
Contents |
DLL Development
All TNI plugin DLLs must be written in C/C++. Due to the code-signing requirements imposed by the TNI developer agreement, it is not possible to create TNI plugin DLLs in other languages. At the current time, all TNI plugin DLLs must compile under Microsoft Visual Studio 2013's subset of C++11. TNI DLLs may use the following external headers and techniques:
- The TrainzNativeInterface SDK headers, which provide the TNI Core Interface and other component interfaces.
- The C standard library allocators and utility functions.
- The C++ STL containers, allocators, and utility functions.
TNI DLLs must not use the following external headers and techniques unless a specific exception is granted.
- Windows Platform SDK.
- File, network, permissions, process, or other "system access" or "external access" headers.
- Third-party DLLs.
- Third-pary source code which does not follow these requirements.
- Assembly language.
- Bytecode machines, JIT compilers, and other forms of state machine whose runtime characteristics cannot be determined in advance; i.e. programs which effectively treat input data as source code.
- The plugin may use C++ exceptions internally, but must not allow exceptions to be propagated back to the calling code.
- The plugin must not throw or attempt to catch native exceptions (win32 exceptions, posix signals, etc.)
TrainzNativeInterface.h
The TNI core interface is designed to abstract the passing of data between Trainz and the TNI plugin DLLs. All data is represented by an opaque polymorphic object basetype, TNIObject. The objects are reference counted, with simple reference-count rules:
- Ownership of function parameters is held by the caller throughout the function call. They may be retained by the callee if they are required to persist after the function returns. The callee is then responsible for ensuring that they are released at a later time. For mutable object types such as TNIArray, the caller should not modify the array after the callee returns, since it has no way to know if the callee has kept a reference to the array- instead, it should release its own reference to the array. Exceptions are permitted where shared access is considered beneficial, but these must be clearly documented at every use site.
- Ownership of function return values is passed to the caller. For mutable object types such as TNIArray, the callee should not keep its own reference to the return value since the caller could further modify the object.
The following TNIObject-derived classes are provided by the TNI Core Interface:
TNIObject
TNIObject is the polymorphic base class for all the entire TNI object hierarchy. All TNIObjects are considered opaque types, are constructed via helper functions provided in the TrainzNativeInterface DLL, and are released with TNIRelease().
// ============================================================================
// TNI OBJECT TYPES
// ============================================================================
enum
{
TNI_CLASS_NONE = 0,
TNI_CLASS_OBJECT = 1,
TNI_CLASS_STRING = 2,
TNI_CLASS_ARRAY = 3,
TNI_CLASS_CONTEXT = 4,
TNI_CLASS_LIBRARY = 5,
TNI_CLASS_STREAM = 6,
TNI_CLASS_GRAPHICS = 7,
TNI_CLASS_VECTOR = 8,
TNI_CLASS_ASSETID = 9,
TNI_CLASS_LABEL = 10,
TNI_CLASS_BUFFER = 11,
TNI_CLASS_SOUP = 12,
};
// ============================================================================
// Name: TNIGetObjectClass
// Desc: Return the type of the specified object, as per the above enumeration
// values.
// Parm: object - the object to query. It is safe to pass a NULL value here.
// ============================================================================
uint32_t TNIGetObjectClass(const TNIObject* object);
// ============================================================================
// Name: TNIReference
// Desc: Add a reference to the specified object. The object will be released
// when the reference count drops to zero.
// Parm: object - The object to which we will add a reference. It is safe to
// pass a NULL value here.
// ============================================================================
void TNIReference(const TNIObject* object);
// ============================================================================
// Name: TNIRelease
// Desc: Remove a reference to the specified object. The object will be
// released when the reference count drops to zero. TNIRelease() should
// be called exactly once for each call to TNIReference() and once for
// any function return value (for example, from TNIAllocString().)
// Parm: object - The object from which we will remove a reference. It is safe
// to pass a NULL value here.
// ============================================================================
void TNIRelease(const TNIObject* object);
TNIString
TNIString provides a simple string class. The raw string data is zero-terminated in the usual C fashion, and is encoded as UTF-8. UTF-8 is a superset of ASCII, so ASCII strings can be stored directly in a TNIString with no conversion necessary.
// ============================================================================
// Name: TNIAllocString
// Desc: Allocates a TNIString given a zero-terminated UTF8 text buffer.
// Parm: utf8text - The UTF-8 text buffer. The contents of the buffer are
// copied into the TNIString. The function results are undefined if this
// parameter does not represent a valid UTF-8 encoded string.
// Retn: TNIString - The newly allocated TNIString.
// Note: You must eventually release the allocated string using TNIRelease().
// Note: If 'utf8text' is nullptr, the returned TNIString is an empty string.
// ============================================================================
TNIString* TNIAllocString(const char* utf8text);
// ============================================================================
// Name: TNIGetStringText
// Desc: Retrieves a pointer to the internal text buffer of the TNIString
// object. This is a read-only reference, and remains valid only while
// (1) you hold a reference to the TNIString, and (2) you do not modify
// the TNIString.
// Parm: string - The TNIString object to query.
// Retn: const char* - A temporary pointer to the internal string text, in
// utf8 encoding.
// Note: If 'string' is a nullptr, the return value is a pointer to an empty
// string.
// ============================================================================
const char* TNIGetStringText(const TNIString* string);
// ============================================================================
// Name: TNIGetStringSize
// Desc: Returns the size in bytes of the specified string.
// Parm: string - A TNIString object, or nullptr.
// Retn: size_t - The size in bytes of the text string. Note that this is the
// size of the UTF-8 encoded text data, not including zero termination,
// and not including any overheads. If the string is null or empty, this
// returns zero.
// ============================================================================
size_t TNIGetStringSize(const TNIString* string);
// ============================================================================
// Name: TNISetStringText
// Desc: Modifies the TNIString to a copy of the supplied UTF-8 encoded text
// buffer.
// Parm: string - The existing TNIString object to modify. Modifying the string
// invalidates any pointer previously returned by TNIGetStringText().
// Parm: utf8text - The UTF-8 text buffer. The contents of the buffer are copied
// into the TNIString. The function results are undefined if this
// parameter does not represent a valid UTF-8 encoded string.
// Note: If 'string' is a nullptr, this function does nothing.
// Note: If 'utf8text' is a nullptr, it is considered an empty string.
// ============================================================================
void TNISetStringText(TNIString* string, const char* utf8text);
// ============================================================================
// Name: TNIAppendStringText
// Desc: Appends the supplied UTF-8 encoded text buffer to the TNIString.
// Parm: string - The existing TNIString object to modify. Modifying the string
// invalidates any pointer previously returned by TNIGetStringText().
// Parm: utf8text - The UTF-8 text buffer. The contents of the buffer are copied
// into the TNIString. The function results are undefined if this
// parameter does not represent a valid UTF-8 encoded string.
// Note: If 'string' is a nullptr, this function does nothing.
// Note: If 'utf8text' is a nullptr, this function does nothing.
// ============================================================================
void TNIAppendStringText(TNIString* string, const char* utf8text);
TNILabel
TNILabel provide an immutable string class which is designed to provide an efficient enumeration. Only one TNILabel object can exist for any unique string, so these objects can be rapidly compared by pointer equality. It is intended that you will create one TNILabel object for each enumeration during initialisation, and will then use these objects rather than creating new objects at runtime.
TNIAssetID
TNIAssetID encodes a KUID or other Asset ID. You should avoid making any assumptions about the meaning of the data encoded in an TNIAssetID, as the interpretation is subject to change without notice.
TNIArray
TNIArray provides an indexed element array containing TNIObjects. Space for the entire array is allocated when the size is set. Random access within the array is very fast.
TNISoup
TNISoup provides a key-value mapping, similar in concept to the class Soup and the config.txt file format. Keys are TNILabel objects, and values are TNIObjects.
TNIVector
TNIVector provides an immutable indexed element array containing 32-bit floating point values. Values for the entire vector are defined during construction. Random access within the vector is very fast.
TNIContext
TNIContext provides the operating context in which the TNI plugin is operating. Trainz supports multiple concurrent TNI Contexts, and DLLs are typically shared between contexts, so the plugin must correctly track which context any given object or function call is made in. The TNI plugin should not leak data between contexts, although it may share non-context-specific resources internally in a manner which is opaque to the parent context. Each TNIContext is likely to run in a different thread of execution, although this is not guaranteed. Any internal storage maintained by the TNI plugin should be keyed to its parent context to prevent race conditions and other threading concerns. Access to private shared state must be protected by appropriate thread-safe locks.
TNILibrary
The TNI plugin DLL entry point is expected to construct a TNILibrary descriptor and return it to Trainz. This serves as the public interface to the plugin, and may be accessed by Trainz native code, TrainzScript, and other TNI plugins. TNILibrary objects are context-specific, so multiple TNILibrary objects may be created for a single plugin DLL.
The plugin DLL may be unloaded at any point while there are no TNILibrary objects in existence for that plugin. The plugin should create any shared state when the first TNILibrary object is created, and release any shared state when the last TNILibrary object has is released.
TNIBuffer
TNIBuffer provides untyped binary data storage. This is used in scenarios where large amounts of complex-type data needs to be rapidly created and passed between systems. This should be considered a last-resort mechanism, used only when the cost of the other object types is prohibitive.
TNIStream
TNIStream provides a mechanism for serialising both data and TNIObjects into a "command buffer" which can be passed to another system. This is used in scenarios where large numbers of commands must be rapidly packed and later replayed. The writer and reader must agree on the stream format- the reader must make the appropriate calls to match what the writer used when constructing the stream. It is strongly recommended that some form of version negotiation is used to allow future changes to any streamed formats. TNIObjects are written to a TNIStream by reference- the TNIObjects themselves are not serialised or copied.
This should be considered a last-resort mechanism, used only where the cost of the other object types is prohibitive.