TrainzScript Message Reference
Contents |
What are Messages and how can they be used?
Messages:
- Are internal communications between objects within Trainz.
- Are completely hidden from the normal user, who won't be aware of their existence.
- Provide notification of events within the Trainz script environment.
- Provide a means of opening direct communications between objects.
- Provide a means of sending instructions from one script to another.
- Provide a means of timing scripted actions.
Messages are managed by the Router, a sort of internal post office, and can be exchanged between any objects which descend from GameObject, this includes driver commands, rules and libraries as well as almost all mesh based scriptable objects.
Scripts can:
- Listen for messages and use them to take appropriate actions in response to events and instructions.
- Use the message to extract information about the object sending or receiving the message.
- Define and issue their own messages.
- Listen in to messages intended for other objects.
- Send messages to themselves, to arrive after a specified delay, as a 'reminder' to do something at a future time.
Messages are small data objects containing five pieces of information:
- msg.major: A string identifying the message type.
- msg.minor: A string containing the message subtype or additional information.
- msg.src: A reference to the object that sent the message.
- msg.dst: The intended recipient. May be null if the message was broadcast (legacy behavior, no longer supported).
- msg.paramSoup: A Soup object containing extra data about the message (may be null).
How to listen for Messages
There are three techniques you can use:
- AddHandler() This is the simplest listening device and is most useful when you know that you will always be interested in this type of message. AddHandler() is usually set up in the Init() method of a script. Hint: omit any optional spaces in statements that include an AddHandler function, as including them causes the function to be ignored without notification in TRS2004 and perhaps other versions as well.
- A wait() loop. This needs to run in a separate thread and allows for a temporary arrangement where you want to know about messages for a shorter period or under a specified set of conditions.
- Sniff(). This allows you to 'eavesdrop' on messages being sent to another object.
When and how to send Messages
You might consider issuing messages of your own under the following circumstances:
- When an event has taken place in your script and other objects might need to know about it.
- When you want to make your object available to other scripts to allow them to call your methods directly.
- When you need to issue instructions to another object to carry out a particular task and you know that the target object will be able to understand your message.
- When you want your own script to take an action at some time in the future, or at regular intervals.
There are three routines you can use to issue messages:
- SendMessage() dispatches the message immediately.
- PostMessage() dispatches the message via the Router with a specified delay (which can be zero). The message will be executed on the next Router update after the delay has elapsed.
- Router.PostMessage() allows messages to be dispatched even if the object you are scripting doesn't have its own messaging capabilities.
If the message destination is entered as me (meaning the current object) the message will be sent to the object which owns the script. If the destination is null the message will be broadcast and therefore available to any object in the session. Message broadcasting is obsolete as of TRS19 however, and may result in script exceptions or silent failures (depending on the local players compatibility setting).
If you have a reference to the message target (or if you know its name or id) you should consider the following:
- If you know which methods to use it might be more appropriate to call the other script directly.
- You should always avoid broadcasting messages if it is possible to send them directly to the intended recipient.
Never Assume Anything
The messaging system contains many traps for the unwary. If you are working with a junction for instance you will receive an Object,Enter message whenever a train enters your object's trigger zone. It is not safe however to assume that a subsequent Object,Leave means that the train has crossed the junction and left on the other side. Any of the following would also produce Object,Leave:
- The train might have stopped and reversed away from your object.
- An intervening junction might have been toggled so that the train is no longer visible to your object.
- The train might have coupled to another (and therefore ceased to exist) before reaching your object.
It is usually necessary to treat a message as an indication that the event you are interested in could have happened and to use other tests to confirm.
Example
Listening for messages and using message handler methods
include "MapObject.gs" // script can be attached to any scenery object // enable the message window to view the output in Driver mode class Messenger isclass MapObject { public void Init(void) { inherited(); // listen for Junction and World,ModuleInit messages AddHandler(me,"Junction","","ToggleHandler"); AddHandler(me,"World","ModuleInit","ModuleInitHandler"); } thread void TriggerMonitor(void) { // Trigger messages will be passed to us as a result of the // Sniff() call in ModuleInitHandler Message msg; wait() { // listen for Trigger,Enter, print a message when heard, continue listening on "Trigger","Enter",msg: { Interface.Print("A train has entered a trigger"); continue; } // listen for Trigger,Stopped, print a message when heard and then quit the thread on "Trigger","Stopped",msg: { Interface.Print("A train has stopped on a trigger"); Interface.Print("Trigger monitoring discontinued"); break; } } } void ToggleHandler(Message msg) { // print a message whenever Junction,Toggled is broadcast Interface.Print("A junction has been toggled"); } void ModuleInitHandler(Message msg) { if (World.GetCurrentModule() == World.DRIVER_MODULE) { // attach a handler to the first train on the map instructing it // to copy any Trigger messages to us Sniff(World.GetTrainList()[0],"Trigger","",true); // start monitoring Trigger messages TriggerMonitor(); } } };