To do this, you’d need some form of client-server communication, aka a remote, which is what TS+ does on initialization.
Now, you have a remote, but you need to be able to communicate between client and server in the same module.
Luckily though, it’s pretty easy to do this with RunService - it has functions called :IsClient() and :IsServer() and returns true if what the module is running on is the client or server, obviously.
For a module to run, you just need to require it, which is why modules like this need you to require it somewhere on the client. Modules can actually run separate regular code inside of them (stuff outside what is returned) which is the whole reason why you’re able to control both sides in the same module.
So for example if you had a module like this:
local module = {}
print("HI")
return module
and required it somewhere, the code would print.
So applying this to allow you to control both sides in the same module would look like this:
local rs = game:GetService("RunService")
local repStorage = game:GetService("ReplicatedStorage")
local remote -- Make an empty variable, because this needs to be referenced differently depending on where the module is running
local moduleTest = {}
if rs:IsServer() then -- if this is running on the server,
remote = Instance.new("RemoteEvent") -- Create the remote on runtime, make sure it's on the server!
remote.Name = "Test"
remote.Parent = repStorage -- This can be wherever you want
elseif rs:IsClient() then
remote = repStorage:WaitForChild("Test") -- Have the client wait for that same remote the server will create
end
-- At this point, now you have access to both the remote on the server and client.
-- Since like I said, modules can run regular code just be being required, you
-- can do this in the same module
if rs:IsClient() then -- have the client connect to the remote
remote.OnClientEvent:Connect(message)
print("Received message:", message)
end
end
-- Now, we can make a function in the module that fires the same remote. Only the server can fire remotes, so we do a check, too.
function moduleTest.sendMessage(message)
if not rs:IsServer() then return end
remote:FireAllClients(message)
end
return moduleTest
Now that you have the module done, it’s easy.
Require it somewhere on the client so that the client code runs (you don’t even need to store it in a variable or anything):
require(game:GetService("ReplicatedStorage"):WaitForChild("ModuleTest")) -- wherever the module is
-- Do it in either StarterCharacterScripts or StarterGui so that the code
-- refreshes on respawns
Then, you can use the module wherever you want on the server and all the client code picks up the actions.
local testModule = require(game:GetService("ReplicatedStorage"):WaitForChild("ModuleTest"))
wait(20)
testModule.sendMessage("Hello from the server")
And there you have it! The client should be able to pick up the code, and you’re controlling both sides from one module.