A simple function for syncing ModuleScript method calls when it's required by multiple Actors

This function aims to drastically decrease the amount of boilerplate needed when requiring modules from different actors. It automatically replaces method calls with bindable function invokes when the module isn’t required by the main thread (first require call), rerouting your method calls to the main thread. This means you can safely use variables instead of having to store all your data in shared tables and instance properties/attributes.

Note that this isn’t perfect. Since it uses BindableFunctions, the performance isn’t amazing, sending functions isn’t possible, and you’ll only get copies if you try to parse a table (SharedTables will work properly). It also doesn’t replicate value changes, so you’ll have to implement getter and setter functions for them. But not only does it potentially replace dozens of redundant lines with just 1 or 2, it also allows you to write your code in a more Lua-friendly manner.

The function itself:

return function(Script:ModuleScript, Module:any, Exceptions:{string}?)
	Exceptions = Exceptions or {};
	task.synchronize();
	if (Script:GetAttribute("PARALLELPROTECTED")) then
		if (not Script:GetAttribute("PARALLELLOADED")) then Script:GetAttributeChangedSignal("PARALLELLOADED"):Wait(); end
		local Bindables = Script:FindFirstChild("_bindables");
		for Index, Value in Module do
			if (table.find(Exceptions, Index) or type(Value)~="function") then continue; end
			local Bindable = Bindables:FindFirstChild(Index) :: BindableFunction;
			Module[Index] = function(...)
				return Bindable:Invoke(...);
			end
		end
		return true;
	else
		Script:SetAttribute("PARALLELPROTECTED", true);
		local Bindables = Instance.new("Folder");
		Bindables.Name = "_bindables";
		Bindables.Parent = Script;
		for Index, Value in Module do
			if (table.find(Exceptions, Index) or type(Value)~="function") then continue; end
			local Bindable = Instance.new("BindableFunction");
			Bindable.Name = Index;
			Bindable.OnInvoke = Value;
			Bindable.Parent = Bindables;
		end
		Script:SetAttribute("PARALLELLOADED", true);
		return false;
	end
end

A short documentation:

Arguments:
	Script: The ModuleScript instance you want to sync
	Module: The value returned by said ModuleScript
	Exceptions (optional): An array of names of the methods you don't want to replace.
		Use this for methods that don't have to be synced.
Returns: RequiredInParallel

Example of usage:

-- the modulescript you want to sync
local Module = {};
local test = 123;
function Module:AddNumber(NumberToAdd:number)
	test += NumberToAdd;
	print(test);
end
-- the only line needed
require(path.to.syncparallel)(script, Module);
return Module;

-- script in 1st actor
local Module = require(path.to.module);
Module:AddNumber(1); --> 1

-- script in 2nd actor
local Module = require(path.to.module);
Module:AddNumber(1); --> 2
3 Likes