What is the best way for module communication?

What is the best way to avoid “Circular Dependency” and make the modules communicate with each other efficiently?

Let’s say that I have 8 different modules and some of them need to communicate back and forth with each other. I tried to “trick” the system by making some sort of a “Mediator” by having one single module that would require all of them, and inside each module, it would require that “Mediator” module but it didn’t work. 

The other form of communication I could think of is having some sort of a “Messaging” system. But I have no idea how would I implement something like that.

I don’t want something that is fancy, I just need my modules to access each other in the most simple way possible. 

The easiest ways to avoid circular dependency are:
 

  1. Don’t do it.  i.e. If modules need to ‘communicate’, use a scratch pad module they both require to do the talking.
     
    This is OK for sharing data, but what about function calls?  
     

  2. Again, you should try to avoid it, but it is totally OK for modules to cross-require each other as long as you do it just in time.
     
     
    This is WRONG and will not work:

    // File m1.lua local m2 = require “m2” local m1 = {} function m1.doA() print(“A”) m2.doB() end function m1.doC() print(“C”) end return m1

    // File m2.lua local m1 = require “m1” local m2 = {} function m1.doB() print(“B”) m1.doC() end return m2

 
 
This is BETTER / SAFER:

// File m1.lua local m1 = {} function m1.doA() local m2 = require "m2" print("A") m2.doB() end function m1.doC() print("C") end return m1

// File m2.lua local m2 = {} function m1.doB() local m1 = require "m1" print("B") m1.doC() end return m2
  1. For cross module communication I often I prefer using events. 

This is why SSK2 provides shortcut functions for the Runtime:* features.

https://roaminggamer.github.io/RGDocs/pages/SSK2/globals/#runtime-improvements

Warning!   Doing this means you need to clean up later. Runtime event listeners do not get cleaned up automatically.

Thanks for the help. Your solution inspired me to make something like this, but I’m not sure if it’s the most efficient or if it has some side effects that I haven’t yet discovered. But it works:

GeneralModule.lua

local M = {} function M.M1(  ) local mod = require "M1"  return mod end function M.M2(  ) local mod = require "M2"  return mod end function M.M3(  ) local mod = require "M3"  return mod end function M.M4(  ) local mod = require "M4"  return mod end return M

The above is a file dedicated to just requiring all other modules in functions. I then require this file in each module. 

One thing I noticed is that the simulator became a bit slower when first loading the project but then went back to being quick when I made the variable of the  GeneralModule ( the above file ) a global one instead of localised in each file.

The other thing I’m not sure about is If I changed a value inside a certain module, that won’t change it in the original module, would it? Example:

local GeneralModule = require "GeneralModule" local m1 = GeneralModule.M1() M1.temp = 3

Would “temp” get changed to 3 in the original file or stay as it’s default value? Is “m1” here a reference or a copy?

EDIT: Yes, it does change the value in the original module. 

Hi,

Everyone has their own preferred method, but I have to say that I have found the, for me, perfect code and module organisation.

Basically, make a hierarchical tree, with a “parent” on top, and “children”, “grandchildren” and so on descending from this parent. You also give EACH object it’s own displayGroup that follows the hierarchy, unless there is nothing visual going on.

I have an example PDF in this thread that visualises this:

https://forums.coronalabs.com/topic/70548-hey-my-name-is-hannes/

The trick is that you need to create a master module on top, and then when you create a child module underneath the master, you need to give this child module a reference to it’s parent. I store this reference in a variable called “super”, in each child object.

That way every object can talk to eachother by going up and down the hierarchical tree.

“Higher” objects in the tree can call their children and grandchildren (and so on) by calling their name like this, going down the tree:

myLevelController.myEnemyManager.enemyList[1].myBulletManager.bulletList[1].endLife()

Going up the tree does not read as clearly, because every child calls it’s parent “super”, so if you go up a couple of levels it looks a bit weird:

“bulletList[1].super.super.super.super” is actually the levelController object described above.

It takes a while to get used to this, but once you get the hang of it, it works really, really well!

Let me know if you have any questions, but first check out the thread I mentioned.

By the way, the method I describe above let’s all objects talk to any other object directly, so you don’t need to fire events into the empty vast space.

Most of all, once you have every object in one big tree, it becomes really easy to clean up everything, because the principle and structure is always the same.

As a last benefit, the “tree” structure makes it really easy to visualise and design your code architecture, especially when including the displayGroups.

As I said, everyone has their favourite code architecture, and this is only my personal opinion, but I really think this is the most logical and practical code architecture for any project.

This approach works fine as long as children don’t start accessing other children via “super”.

e.g.  bullet[1].super.bullet[2]

Debugging that stuff is a total nightmare!  As long as access is (limited to) vertical there is no real issues.

An easy way to visualize is - Game world owns player owns gun owns bullets.

Hi SGS,

As mentioned above, it comes down to personal preference, and everyone is free to work as he or she prefers, but I am going to argue against your point! :slight_smile: For the record: I’m sort of countering you argument because I’d like other forum visitors to get a decent overview of the matter so they can for their own opinion based on their specific needs or project.

So, that being said, I don’t see why “children accessing other children” would be an issue or a debugging nightmare, to be honest. Quite the opposite, actually, in my opinion.

First of all, for specific types of objects, I have “manager” super objects that have overview of their children. An example of this is an “enemiesManager” class. Any functionality that requires siblings to talk to each other can happen directly between two children without issue in my opinion, or can be dealt with by the manager object above these siblings. In a way, the “gun” object you mention above could also be this manager object. My manager objects deal with collective creation & removal, and all functions that need to deal with “all bullets”, or all objects managed by this object, typically held in an array.

If the issue is semantics, you have a slight point, but that is easily dealt with. You can create a simplified handle at the start of your module for objects that are referenced often, and use that in further code, like this:

local heroObject = bullet.super.super.super.super

Or if you feel the repeating “super” is confusing, you could give these classes specific names, like this:

bullet.myBulletManager.myHeroCharacter.myLevelController.myAppEngine

Second, in many parts of game programming there is little “lateral” communication ( i.e. communication between different children of the same parent) between bullets, enemies or other types of objects. 99% of the time my communication between different children of the same parent is between objects with substantially different functionality, e.g. a soundManager object (or class or module) and a levelController object.

In this case, communicating between the different children of the same parent is super-easy. My soundManager talks to the levelController like this, through their parent “appEngine” object:

self.super.levelController.finishLevel()

And my levelController talks to the soundManager like this:

self.super.soundManager.playSound()

If you feel “self.super” reads to difficultly, you can always create a more legible reference like:

myAppEngine.levelController.finishLevel()

Third and lastly, I think many alternative code architectures don’t really offer any good way of talking to siblings at all. Event and messaging systems are a substitute, but don’t offer enough control for me. Instead, If one of the child objects needs to trigger a general event, it asks it’s parent or grandparent to execture a specific part of code, and this code bubbles down to all needed objects.

This way I have a perfect overview of what objects receive “the message” first - something that is often hard to keep track of without a hiërarchical structure.

I could go on! :smiley: But I’m not going to. But I can say this: for platform games, this architecture really is brilliant, and in all honesty I’ve never coded a project where this architecture did not work soooooo smoothly and easily. One of the reasons I keep going on here is that (for me) it really is a joy to work this way, and I want to spread the good word! :lol:

To the original poster: what type of game or application are you trying to make? More often than not, knowing this will often result in seeing what architecture is a) possible and b) optimal for your project, so feel free to share.

In a single user environment “children accessing other children” is not so bad but in a multi-user environment where other developers could add what they consider a feature can cause major headaches for large code bases as this brings in unexpected behavior.  A classic example is a child destroying another child and the parent ending up with invalid pointers.

A common use-case is for the parent to spawn children and keep control of where the children are and what they are doing.  Now if a child has manually updated state for other children (for example, it’s position) then this becomes out of sync to the parent and this can cause functions and look-ups  against the parent to provide inaccurate data or worse.

IMHO, if a child needs to alter state in another then that change should be propagated via the controller so the controller is aware of this change.

Hi SGS,

Good point about the the multi-developer environment. I develop solo, and always

Fully agree, in that changes should be propagated via the controller. But for me this is another argument pro hierarchical tree architecture, and contra event dispatching.

Of course, as with any architecture, some coding discipline is required, and indeed, as per your example a child destroying another child is something to be avoided at all cost. If really needed, you could require passing the “source of the destruction command” as a parameter, and then refuse to be destroyed if the source is not an accepted controller object.

But for my single developer environment that’s a bit of overkill, and just maintaining some proper coding hygiene takes care of things.

At the risk of sound like a broken record on this forum: check out the PDF and sample code I uploaded in this thread. This outlines what in my view is the “best” way to structure your code in modules.

Disclaimer: I know there is no single best way for every project or developer. But I do know that if there was, it would be my way :smiley:

Just kidding! But do check it out - working this way has made my coding skills soooooo much better.

Some more detailed info: what I like about the structure I use is this:

  • Code is split into logical blocks that are as small as possible

  • Any element within my app can talk to any other element

  • The code structure and construction and cleanup routines are the same for every module

The above are the 3 main reasons for structuring my modules the way I do, and they speed up my development tremendously.

The easiest ways to avoid circular dependency are:
 

  1. Don’t do it.  i.e. If modules need to ‘communicate’, use a scratch pad module they both require to do the talking.
     
    This is OK for sharing data, but what about function calls?  
     

  2. Again, you should try to avoid it, but it is totally OK for modules to cross-require each other as long as you do it just in time.
     
     
    This is WRONG and will not work:

    // File m1.lua local m2 = require “m2” local m1 = {} function m1.doA() print(“A”) m2.doB() end function m1.doC() print(“C”) end return m1

    // File m2.lua local m1 = require “m1” local m2 = {} function m1.doB() print(“B”) m1.doC() end return m2

 
 
This is BETTER / SAFER:

// File m1.lua local m1 = {} function m1.doA() local m2 = require "m2" print("A") m2.doB() end function m1.doC() print("C") end return m1

// File m2.lua local m2 = {} function m1.doB() local m1 = require "m1" print("B") m1.doC() end return m2
  1. For cross module communication I often I prefer using events. 

This is why SSK2 provides shortcut functions for the Runtime:* features.

https://roaminggamer.github.io/RGDocs/pages/SSK2/globals/#runtime-improvements

Warning!   Doing this means you need to clean up later. Runtime event listeners do not get cleaned up automatically.

Thanks for the help. Your solution inspired me to make something like this, but I’m not sure if it’s the most efficient or if it has some side effects that I haven’t yet discovered. But it works:

GeneralModule.lua

local M = {} function M.M1(  ) local mod = require "M1"  return mod end function M.M2(  ) local mod = require "M2"  return mod end function M.M3(  ) local mod = require "M3"  return mod end function M.M4(  ) local mod = require "M4"  return mod end return M

The above is a file dedicated to just requiring all other modules in functions. I then require this file in each module. 

One thing I noticed is that the simulator became a bit slower when first loading the project but then went back to being quick when I made the variable of the  GeneralModule ( the above file ) a global one instead of localised in each file.

The other thing I’m not sure about is If I changed a value inside a certain module, that won’t change it in the original module, would it? Example:

local GeneralModule = require "GeneralModule" local m1 = GeneralModule.M1() M1.temp = 3

Would “temp” get changed to 3 in the original file or stay as it’s default value? Is “m1” here a reference or a copy?

EDIT: Yes, it does change the value in the original module. 

Hi,

Everyone has their own preferred method, but I have to say that I have found the, for me, perfect code and module organisation.

Basically, make a hierarchical tree, with a “parent” on top, and “children”, “grandchildren” and so on descending from this parent. You also give EACH object it’s own displayGroup that follows the hierarchy, unless there is nothing visual going on.

I have an example PDF in this thread that visualises this:

https://forums.coronalabs.com/topic/70548-hey-my-name-is-hannes/

The trick is that you need to create a master module on top, and then when you create a child module underneath the master, you need to give this child module a reference to it’s parent. I store this reference in a variable called “super”, in each child object.

That way every object can talk to eachother by going up and down the hierarchical tree.

“Higher” objects in the tree can call their children and grandchildren (and so on) by calling their name like this, going down the tree:

myLevelController.myEnemyManager.enemyList[1].myBulletManager.bulletList[1].endLife()

Going up the tree does not read as clearly, because every child calls it’s parent “super”, so if you go up a couple of levels it looks a bit weird:

“bulletList[1].super.super.super.super” is actually the levelController object described above.

It takes a while to get used to this, but once you get the hang of it, it works really, really well!

Let me know if you have any questions, but first check out the thread I mentioned.

By the way, the method I describe above let’s all objects talk to any other object directly, so you don’t need to fire events into the empty vast space.

Most of all, once you have every object in one big tree, it becomes really easy to clean up everything, because the principle and structure is always the same.

As a last benefit, the “tree” structure makes it really easy to visualise and design your code architecture, especially when including the displayGroups.

As I said, everyone has their favourite code architecture, and this is only my personal opinion, but I really think this is the most logical and practical code architecture for any project.

This approach works fine as long as children don’t start accessing other children via “super”.

e.g.  bullet[1].super.bullet[2]

Debugging that stuff is a total nightmare!  As long as access is (limited to) vertical there is no real issues.

An easy way to visualize is - Game world owns player owns gun owns bullets.

Hi SGS,

As mentioned above, it comes down to personal preference, and everyone is free to work as he or she prefers, but I am going to argue against your point! :slight_smile: For the record: I’m sort of countering you argument because I’d like other forum visitors to get a decent overview of the matter so they can for their own opinion based on their specific needs or project.

So, that being said, I don’t see why “children accessing other children” would be an issue or a debugging nightmare, to be honest. Quite the opposite, actually, in my opinion.

First of all, for specific types of objects, I have “manager” super objects that have overview of their children. An example of this is an “enemiesManager” class. Any functionality that requires siblings to talk to each other can happen directly between two children without issue in my opinion, or can be dealt with by the manager object above these siblings. In a way, the “gun” object you mention above could also be this manager object. My manager objects deal with collective creation & removal, and all functions that need to deal with “all bullets”, or all objects managed by this object, typically held in an array.

If the issue is semantics, you have a slight point, but that is easily dealt with. You can create a simplified handle at the start of your module for objects that are referenced often, and use that in further code, like this:

local heroObject = bullet.super.super.super.super

Or if you feel the repeating “super” is confusing, you could give these classes specific names, like this:

bullet.myBulletManager.myHeroCharacter.myLevelController.myAppEngine

Second, in many parts of game programming there is little “lateral” communication ( i.e. communication between different children of the same parent) between bullets, enemies or other types of objects. 99% of the time my communication between different children of the same parent is between objects with substantially different functionality, e.g. a soundManager object (or class or module) and a levelController object.

In this case, communicating between the different children of the same parent is super-easy. My soundManager talks to the levelController like this, through their parent “appEngine” object:

self.super.levelController.finishLevel()

And my levelController talks to the soundManager like this:

self.super.soundManager.playSound()

If you feel “self.super” reads to difficultly, you can always create a more legible reference like:

myAppEngine.levelController.finishLevel()

Third and lastly, I think many alternative code architectures don’t really offer any good way of talking to siblings at all. Event and messaging systems are a substitute, but don’t offer enough control for me. Instead, If one of the child objects needs to trigger a general event, it asks it’s parent or grandparent to execture a specific part of code, and this code bubbles down to all needed objects.

This way I have a perfect overview of what objects receive “the message” first - something that is often hard to keep track of without a hiërarchical structure.

I could go on! :smiley: But I’m not going to. But I can say this: for platform games, this architecture really is brilliant, and in all honesty I’ve never coded a project where this architecture did not work soooooo smoothly and easily. One of the reasons I keep going on here is that (for me) it really is a joy to work this way, and I want to spread the good word! :lol: