Call require() for the same ModuleScript multiple times

I have a ModuleScript that is a base implementation of a sword in my game.

I have other sword scripts that require this module script and then set various overrides on the base behavior to do things like draw sword-specific particle effects.

I have a bug in my code because I didn’t understand how require works. It’s only called once, globally. I thought I was getting a new script environment each time I called require.

So right now all my swords require this Module Script. The Module Script has local state that I want to be unique to each sword.

Is there a hack that I can use with get/setenv or potentially LoadString to make it so each time I call this Module Script, I get back a separate new instance of it, instead of a shared instance?

I’m basically hacking around Lua not having classes. I want to use require as an object factory instead of whatever it’s doing now (I guess it’s a #include).

8 Likes

You can have a ModuleScript return a function (or library of functions) which constructs an object with its own state. It’s essentially a factory pattern.

return function (tool)
    return {
        tool = tool;
        damage = 15;
        someFunction = function (...) ... end;
    }
end

Usage:

-- Somewhere in a tool far, far away:
local setupTool = require(you_know_what)
setupTool(script.Parent)

This gets super close to the typical Lua style of simulating OOP using metatables, which is a really common pattern used nowadays. It’s probably your best bet here!

7 Likes

Vanilla classic sword script
image

Factory function in Module Script

I think you are right and I need to add a layer of indirection here, so instead of my sword state being local variables of the ModuleScript, they should be returned in a table from Init().

4 Likes

That’s pretty much what you’d want. Most devs would return a table from an init/new function like BaseSword.Init, and set its metatable to a table whose __index is a table of functions you want included with every one of that sword. Like:

local BaseSwordFuncs = {}
function BaseSwordFuncs.Slash()
    -- ...woosh...
    -- (this will be available in all BaseSword)
end

function BaseSword.Init(...)
    ...
   local self = {}
   self.SlashSound = ... -- And all the other juicy sword-local properties here...
   setmetatable(self, {__index = BaseSwordFuncs})
   return self
end

My site has a step by step walkthrough on this if the Programming in Lua book sucks in explaining this (which it does)

3 Likes

If you don’t want to return a function you can also do

require(module:Clone())

to bypass the caching.

3 Likes

Seeing some of these names makes me feel like it’s 2007 again. :smile: (Well, that’s not quite true unless I call you by your former name…)

Another approach I use often is to put state variables in the function environment, and then return a table with accessors, mutators, and other methods. Example:

return function(initialState)
    local state = initialState
    local this = {}
    function this:changeState(newState)
        state = newState
    end
    
    return this
end

I’ve heard it said that this has faster performance at the cost of increased memory usage, but I’d guess that the difference is very minute for most use cases. See here.

3 Likes