Pigeon - easy and safe messaging library for Defold

Hi!

I wanted to share with you a library I’m working with for easy and safe messaging in my Defold projects. At first, I was using Dispatcher by Critique Gaming and Pigeon is its evolution.

It was meant to be my birthday gift to the community, I am only late few days, because, you know, documentation! (at least it is extensive and comprehensive, I hope!) :smiley:

Pigeon is very easy to use, and you can directly replace all your msg.post with it even, but it offers of course much more, main advantages are:

  • Easy to use subscription system
  • Safe checks for defined messages (it can check if data contains proper keys and type is correct)
  • Comes with all Defold system messages defined (so you won’t be able to send a wrong message using it)
  • You can define your own messages as well. Defining message data is optional.
  • You can send messages to all subscribers or to specific urls directly (while still checking data corectness, if the message is defined)
  • You can use strings or hashes. (All strings are pre-hashed internally anyway thanks to amazing Defold-Hashed by @sergey.lerg ).
  • It comes with documentation, examples and some functional tests! :slight_smile:
  • I use the module in Witchcrafter! :grin:

Simplest usage:

pigeon.subscribe("test_message") -- subscribe where you want the message to be received
pigeon.send("test_message") -- send to all subscribers

With message definition (data is verified when sending, so receivers can assume they always get correct data and no additional check have to be done in on_message):

pigeon.define("test_message", {test_value = "string"}) -- define message to require table with at least one key "test_value" of type "string"
pigeon.send("test_message", {test_value = "Hello World!"}) -- message data is verified before sending

Replace Defold’s msg.post with direct sending:

pigeon.send_to(msg.url(), "test_message", {test_value = "Hello World!"})

Then in on_message you can receive messages as usual.

For 'in-depth-ers", there is letters module that defines letters (messages) to be checked by Pigeon. You can directly extend this module, instead of defining every message with pigeon.define() in runtime :slight_smile:

EDIT:
Submitted to Asset Portal :ballot_box_with_check:


Also, I wonder what do you think about setting dependency to log, but explicitly, by user?

I use log everywhere and for the purpose of this library I removed this dependency (but left Defold-Hashed, as it is really small and essential actually). You can replace internal logging system, that by default uses just print() function with log module by @Pkeod with call:

local log = require "log.log"
pigeon.set_dependency_module_log(log)

And it will be printing data with correct level, tag and could also save logs in file, depending on how you set up log :slight_smile:

I already have reworked also DefSave and am thinking about using the same idea for logging there. I looked up this idea a little bit from Monarch, where logging is enabled, by actually assigning function print to internal log, otherwise nothing is happening (logs are disabled).
As logging is generally a good part of any library, this could be a unified way of adding dependency for logging.

What are your thoughs? Are you using log or other modules for logging? :wink:

26 Likes

Thanks for this!

Yay, that’s been a point of annoyance once you reach a high number of custom messages. In a way, this reminds me of moving from JavaScript to TypeScript for large applications.

1 Like

Exactly, it’s only a runtime check, but it’s something that simplifies development a lot and when I’m sure some messages are fixed and correct I can easily replace them with barebone, correct msg.post.

Static languages have advantages over dynamically typed ones, but there are advantages of the latter as well :wink:

This looks great! My main issue with the built-in system was the reliance on hashes and URLs, which can be tedious to work with sometimes.

I can see this being the easy alternative to messages like Monarch is for screen switching with collection proxies.

2 Likes

Thank you for sharing this well-described and useful library! :heart:

I tested and read the code of Pigeon and found several points:

  • If we use the “hook” callback for instant feedback, we should be careful. Since this hook will be called directly from the place where “send” is triggered, it means if we call pigeon.send in go place and catch it in gui, it will result in a context error:
    You can only access gui.* functions and values from a gui script instance (.gui_script file)
    It can be tricky, but we can check the equality of the context like this:
local function send_event(target, event_name)
	local current_url = msg.url()
	current_url.fragment = nil
	local target_url = msg.url(target)
	target_url.fragment = nil

	if current_url == target_url then
		-- One context, we can call functions directly
	else
		-- Different context, we should use msg.post()
	end
end

At least we can show the warning or handle it in some other way.

  • Note that if we set both the “hook” and “url” parameters, only the “hook” parameter will be used.

  • Hook callback functions receive two arguments: the message_id:hash and message:table. It might be useful to pass the first argument parameter in the subscribe method. For example:

local function close_function(self)
    print("We got correct self!", self)
end

function init(self)
     pigeon.subscribe("test", close_function, self)
end

This allows you to avoid creating another closure function. However, it seems not very useful because hooks work only in one context (i.e., one script only and required lua files in this script).

  • The tests from the repository show one failed test:
    DEBUG:SCRIPT: Pigeon tests end -------- [ PASSED: 20 FAILED: 1 ]

  • [minor] The example cannot be run on Defold 1.4.2.

9 Likes

Thank you so much for testing it! :heart: So on which Defold version were you testing? (For me it passes all tests)

I will be looking into all those issues after the jam, thank you for reporting them! :heart_eyes:

1 Like

Test on 1.4.5, just download & run