|
# Messages |
|
|
|
The messages provide a way to run code at opportune moments without unnecessary coupling. Using messages leads to more readable code. |
|
|
|
The messages are identified by a message name, which is often a string, but can be an arbitrary Lua value. |
|
And have an arbitrary number of parameters which are passed to message handlers or unblocked threads. |
|
|
|
When a message is sent, all handlers registered for this particular message are called, |
|
and all threads waiting on the message are resumed. Execution at the sender continues after all the handlers are executed. Message handlers cannot sleep since they are called with a `procall()`. This also means that a lua error in a message handler will not *interrupt* the other handlers or the calling code. |
|
|
|
Messages are used to hook code execution to specific events in the game, |
|
decoupling the sender from the receivers - e.g. when a new map is loaded, or when the game is about to be saved. |
|
Messages are also an efficient way to make a thread sleep until a certain event occurs - |
|
e.g. open a window and resume execution once it is closed is frequently implemented |
|
by waiting on the window object itself, and sending it as a message in its Close() function. |
|
|
|
When a thread is waiting for a certain message it will wake up |
|
from the first message of that type being fired. |
|
If more messages are fired after the first one but before the thread resumed execution, |
|
the thread would have no knowledge of the subsequent messages. |
|
This effect could be both desirable and unexpected. |
|
|
|
The message handlers are executed in the order of registration. |
|
Therefore message handlers registered at file scope will be executed |
|
in the order of file execution (see [Lua startup](LuaStartup.md.html)). |
|
Threads are resumed in the order they became waiting on the message. |
|
|
|
It is possible to register message handlers at any time and in particular after |
|
the startup process. Such handlers are executed after the handlers registered |
|
during startup (at file scope). |
|
|
|
|
|
# Reference |
|
|
|
**Msg(*message*, ...)** |
|
: Sends the message *message* with parameters *...*. *message* can be an arbitrary Lua value except nil - use a string for predefined, "hook"-type of messages, an object for synchronizing threads against its lifecycle, or a specially created empty table when you need a unique token to wait on. |
|
|
|
**PostMsg(*message*, ...)** |
|
: Adds the message *message* to a global queue of messages and wakes up the thread that sends the messages in the queue. The message will be sent by the thread slightly delayed but within the same millisecond it originated. |
|
|
|
**function OnMsg.*message*(...)** |
|
: Registers a message handler function which will be executed whenever *message* is sent. The function receives the parameters passed to the Msg function at the sending site. You can register as many handlers for the same message as you want. |
|
|
|
**WaitMsg(*message*, timeout)** |
|
: Suspends the calling thread (see [Threads](LuaThreads.md.html)) until *message* is sent. Returns remaining time (or true in case there was no timeout) followed by the parameters passed to the Msg function which led to the thread being resumed. If the function times out it returns nothing. |
|
|
|
**MsgClear(*message*)** |
|
: Removes all registered message handlers for *message*. It is used to free memory when it is certain that *message* will never be sent again. (Used for the messages sent during the startup process.) Note that this removes *all* message handlers - it's not possible to remove just a single handler. |
|
|
|
|
|
# Examples |
|
|
|
Notify when the current map changes.<br> |
|
Also shows that messages can be registered at a later time, not only at file scope; such registration will put the message handler after all handlers registered at file scope: |
|
|
|
~~~~~~~~~~ Lua |
|
function OnMsg.Autorun() |
|
function OnMsg.ChangeMap(map_name) |
|
print("Changing map to ", map_name) |
|
end |
|
end |
|
~~~~~~~~~~ |
|
|
|
Runs a function with a timeout.<br> |
|
Returns a positive value and the function's results, nothing in case of a timeout: |
|
|
|
~~~~~~~~~~ Lua |
|
function RunWithTimeout(timeout, func, ...) |
|
local params = {...} |
|
CreateRealTimeThread(function() |
|
Msg(params, func(unpack(params))) |
|
end) |
|
return WaitMsg(params, timeout) |
|
end |
|
~~~~~~~~~~ |
|
|
|
Execute a function at most once per frame: |
|
|
|
~~~~~~~~~~ Lua |
|
CreateGameTimeThread(function() |
|
while true do |
|
WaitMsg("OnRender") |
|
FrameProcess() |
|
end |
|
end) |
|
~~~~~~~~~~ |
|
|
|
Wait until the player has certain amount of a resource; assumes that "ResourcesChanged" is sent when resources are changed: |
|
~~~~~~~~~~ Lua |
|
function WaitResource(resource, amount, timeout) |
|
while Resources[resource] < amount and timeout > 0 do |
|
timeout = WaitMsg("ResourcesChanged", timeout) |
|
end |
|
end |
|
~~~~~~~~~~ |
|
|
|
Parametrised message names: |
|
~~~~~~~~~~ Lua |
|
function PlayerDefeated(player) |
|
Msg("PlayerDefeated " .. player) |
|
end |
|
|
|
function WaitPlayerDefeat(player) |
|
WaitMsg("PlayerDefeated " .. player) |
|
end |
|
~~~~~~~~~~ |
|
|
|
|
|
# List of messages |
|
|
|
There is a partial list of messages definitions including name, parameters and descriptions available in-game as *MsgDef* instances. |
|
|
|
Here's a few of the important messages that are normally sent by the game, listed with their names and parameters: |
|
|
|
Msg("ChangeMap", map) |
|
: Sent when the map change procedure begins. |
|
|
|
Msg("DoneMap") |
|
: Sent when leaving a map. |
|
|
|
Msg("NewMap") |
|
: Sent before objects are loaded on the map. |
|
|
|
Msg("NewMapLoaded") |
|
: Sent after grids and map objects are loaded on the map but before passability calculation. |
|
|
|
Msg("PostNewMapLoaded") |
|
: Sent at the end of the map loading process, after passability calculation and after calling GameInit methods of objects; at this point the map is fully initialized. |
|
|
|
Msg("GameTimeStart") |
|
: Sent when the game time clock starts ticking (see [Threads](LuaThreads.md.html)). |
|
|
|
Msg("OnRender") |
|
: Sent when a frame is rendered. |
|
|
|
Msg("Pause") |
|
: Sent when the game is paused. |
|
|
|
Msg("Resume") |
|
: Sent when the game is resumed. |
|
|
|
Msg("PersistSave", data) |
|
: Sent during the save game process; *data* is a table which will be included in the savegame, so you can add whatever you need to it. |
|
|
|
Msg("PersistLoad", data) |
|
: Sent during the load game process; *data* is the table that was supplied in the *PersistSave* message. |
|
|
|
Msg("LoadGame", metadata) |
|
: Sent after a savegame is loaded. |
|
|
|
Msg("AchievementUnlocked", achievement_id) |
|
: Sent when an achievement is unlocked. |
|
|
|
|
|
# Reactions |
|
|
|
Reactions are a data driven way of reacting to messages and events. |
|
See more in the [Lua reactions](LuaReactions.md.html) page. |
|
|
|
|
|
(insert footer.md.html here) |
|
<style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style><script src="markdeep.min.js" charset="utf-8"></script><script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script> |