Msg.post result received too late (SOLVED)

Hey there!

Well, I have a function and it sends two msg.post messages. First fills a tilemap, and the second draws a path depending on that tilemap built on first msg.post. And it looks like the first msg.post result sometimes late, and the second one got nothing to work with…

Is it normal and I should set some kind of a fuse that continues only when everything’s ready? Or there’s something wrong with the code.

If you send the events from the same function, they will arrive at the same time.

Nope. There’s another function between them. It looks like this:

function endTurn
–msg.post “buildmap”
----msg.post “addMobs”
–msg.post “findPath”

So findPath sometimes not geeting info, addMobs should provide.

What does it look like in practice?

1 Like

Are each of those three messages posted to the same game object?

2 Likes

Something like this:

main.script

function prepareBattle (self)
	msg.post("/map", "createMap")
	msg.post("/ai", "startBattle")
end

map.script

function createMap (self)
	msg.post("/units", "placeMobs") ------------fills tilemap with mobs
	msg.post("/units", "placeCharacters") ------------fills tilemap with characters
end

units.script

function placeMobs (self)
	tilemap.set_tile("/tilemap", "mobs", x, y, mob)
end

function placeCharacters (self)
	tilemap.set_tile("/tilemap", "characters", x, y, character)
end

ai.script

function startBattle (self)
	tilemap.get_tile("/tilemap", "mobs", x, y) --------> nil
	tilemap.get_tile("/tilemap", "characters", x, y) --------> nil
end

And if I put those 2 set_tile lines to map.script it works perfectly. Looks like there’s a stack of messages, and I just don’t understand how it works.

The message system uses a FIFO queue (first in, first out).
When we flush the queue, we pick the first message, dispatch that to the correct handler (e.g. your script).
The handler might push other messages onto the queue, and these will end up last in the queue.

We dispatch messages several times each frame (one tick of the game update loop), once after each component type.

When you add the first two messages, the queue will look like this:

msgs= [("/map", "createMap"), ("/ai", "startBattle")]

After the createMapis received in on_message, you push two more messages:

msgs= [("/ai", "startBattle"), ("/units", "placeMobs"), ("/units", "placeCharacters")]

At this point, it’s clear that the call to startBattle will come before placeMobs/placeCharacters

What you are doing is called asynchronous programming, and that usually needs some kind of synchronization to make sure things happen in the right sequence. E.g. make sure to not post the startBattle until after the map is actually created.

Another way of solving your problem, is to use Lua modules with functionality to manipulate your data. E.g.

local level = require("scripts/level")

function createMap(self)
	level.createMobs(self)
	level.createCharacters(self)
	...
end
3 Likes

Oh, now I got it! Thanks a lot!
Haven’t found it in “Defold message passing” guide. Seems to me like it’s and incredibly helpful info.

Just one more question. To make it work, I need to put those 2 msg.post from prepareBattle to 2 different functions? I.e. prepareBattle runs msg.post("/map", “createMap”), then map.script adds msg.post("/units", “placeMobs”) and msg.post("/units", “placeCharacters”) to message queue. And only after that some function prepareBattle2 adds msg.post("/ai", “startBattle”) to the end of a queue?

It’s probably a good idea to clarify it a bit in the documentation, yes. Thx.

Yes, to make your code work, you need some way to signal that the map creation is done.

One way of doing it:

function createMap (self)
    msg.post("/units", "placeMobs")
    msg.post("/units", "placeCharacters")
    msg.post("/main", "createMapDone") -- send a message back to the main controller script
end
2 Likes

Great! Guess it’s solved. Thanks again, Mathias!