Can you call events in a ModuleScript?

I am using the flag model/system from the default ROBLOX CTF place for a game I am working on.

In its flag handler module, there was a connection connected to when a player joins the game, as seen below.

It worked previously, but recently it broke, and it would not detect when a player joined the game. I simply fixed this, by tying in the function it was supposed to call with my leaderboard script when a player joins the game.

However, this has me wondering - can you even call events in ModuleScripts (outside of a function being called)? As I said before, it was done in the past, but somewhat recently this was changed for me.

You could just call the module script in a server script and call it.

That would be the fastest way to fix it currently as i can think.

It gets called, but the event still never fires.

You’re supposed to connect FlagStandManager.OnPlayerAdded. Also, use a dot, not a semicolon, because this is a static function.

This is what the original looked liked. It still would not fire.

What if the player has joined before the connection was even made?

Try adding this snippet, before the connection:

for _, player in ipairs(Players:GetPlayers()) do
    task.spawn(OnPlayerAdded, player)
end

This still does not work, nothing happens. It just seems like that events will not be called in this ModuleScript, unless otherwise initialized

:man_facepalming: Of course it doesn’t work. I forgot to actually call GetPlayers.

No need to make that a local function.

It’s already been called, earlier up in the script. I posted just snippets from it

And what does it look like now?

It still doesn’t do anything. The solution was to change OnPlayerAdded from a local function to a FlagStandManager.OnPlayerAdded function (as you initially suggested and what I did, except with a : instead of . ) and have it called from a separate server script. Not a big deal, but I wanted to keep this to one script, so I came here to ask if it’s even possible to have a lone event listened, outside the module functions, and have it actually work.

Here’s the working code

local FlagStandManager = {}

-- ROBLOX Services
local Players = game:GetService("Players")

-- Game Services
local Configurations = require(game.ServerStorage.Configurations)

-- Local Variables
local FlagObjects = {}
local FlagCarriers = {}
local Events = game.ReplicatedStorage.serverSidedRemotes
local CaptureFlag = Events.CaptureFlag
local ReturnFlag = Events.ReturnFlag

-- Local Functions
local MakeFlag

local function DestroyFlag(flagObject)
	flagObject.Flag:Destroy()
	for player, object in pairs(FlagCarriers) do
		if object == flagObject then
			FlagCarriers[player] = nil
		end
	end
end

local function OnCarrierDied(player)
	local flagObject = FlagCarriers[player]
	if flagObject then
		local flagPole = flagObject.FlagPole
		local flagBanner = flagObject.FlagBanner
		
		flagPole.CanCollide = false
		flagBanner.CanCollide = false
		flagPole.Anchored = true
		flagBanner.Anchored = true
		
		flagObject.PickedUp = false		
		
		FlagCarriers[player] = nil
		
		if Configurations.RETURN_FLAG_ON_DROP then
			wait(Configurations.FLAG_RESPAWN_TIME)
			if not flagObject.AtSpawn and not flagObject.PickedUp then
				DestroyFlag(flagObject)
				MakeFlag(flagObject)
				ReturnFlag:Fire(flagObject.FlagBanner.BrickColor)
			end
		end
	end
end

local function PickupFlag(player, flagObject)
	FlagCarriers[player] = flagObject
	flagObject.AtSpawn = false
	flagObject.PickedUp = true
	
	local torso
	if player.Character.Humanoid.RigType == Enum.HumanoidRigType.R6 then
		torso = player.Character:FindFirstChild('Torso')
	else
		torso = player.Character:FindFirstChild('UpperTorso')
	end
	local flagPole = flagObject.FlagPole
	local flagBanner = flagObject.FlagBanner
	
	flagPole.Anchored = false
	flagBanner.Anchored = false
	flagPole.CanCollide = false
	flagBanner.CanCollide = false
	local weld = Instance.new('Weld', flagPole)
	weld.Name = 'PlayerFlagWeld'
	weld.Part0 = flagPole
	weld.Part1 = torso
	weld.C0 = CFrame.new(0,0,-1)
end

local function BindFlagTouched(flagObject)
	local flagPole = flagObject.FlagPole
	local flagBanner = flagObject.FlagBanner
	flagPole.Touched:Connect(function(otherPart)
		local player = Players:GetPlayerFromCharacter(otherPart.Parent)
		if not player then return end
		if not player.Character then return end
		local humanoid = player.Character:FindFirstChild('Humanoid')
		if not humanoid then return end
		if humanoid.Health <= 0 then return end
		if flagBanner.BrickColor ~= player.TeamColor and not flagObject.PickedUp then
			PickupFlag(player, flagObject)
			Events.StoleFlag:Fire(player)
		elseif flagBanner.BrickColor == player.TeamColor and not flagObject.AtSpawn and Configurations.FLAG_RETURN_ON_TOUCH then
			DestroyFlag(flagObject)
			MakeFlag(flagObject)
			ReturnFlag:Fire(flagObject.FlagBanner.BrickColor)
		end
	end)
end

function MakeFlag(flagObject)
	flagObject.Flag = flagObject.FlagCopy:Clone()
	flagObject.Flag.Parent = flagObject.FlagContainer
	flagObject.FlagPole = flagObject.Flag.FlagPole
	flagObject.FlagBanner = flagObject.Flag.FlagBanner
	flagObject.FlagBanner.CanCollide = false
	flagObject.AtSpawn = true
	flagObject.PickedUp = false
	BindFlagTouched(flagObject)
end

local function BindBaseTouched(flagObject)
	local flagBase = flagObject.FlagBase
	flagBase.Touched:Connect(function(otherPart)
		local player = Players:GetPlayerFromCharacter(otherPart.Parent)
		if not player then return end
		if flagBase.BrickColor == player.TeamColor and FlagCarriers[player] then
			CaptureFlag:Fire(player)
			local otherFlag = FlagCarriers[player]
			DestroyFlag(otherFlag)
			MakeFlag(otherFlag)
		end
	end)
end

function FlagStandManager.OnPlayerAdded(player)
	print("this should work.")
	player.CharacterAdded:Connect(function(character)
		print("this should work too.")
		character:WaitForChild('Humanoid').Died:Connect(function() OnCarrierDied(player) end)
	end)
	player.CharacterRemoving:Connect(function()
		print("this should work, three!")
		OnCarrierDied(player)
	end)
end

-- Public Functions
function FlagStandManager:Init(container)
	local flagObject = {}
	
	local success, message = pcall(function()
		flagObject.AtSpawn = true	
		flagObject.PickedUp = false
		flagObject.TeamColor = container.FlagStand.BrickColor
		flagObject.Flag = container.Flag
		flagObject.FlagPole = container.Flag.FlagPole
		flagObject.FlagBanner = container.Flag.FlagBanner
		flagObject.FlagBase = container.FlagStand
		flagObject.FlagCopy = container.Flag:Clone()	
		flagObject.FlagContainer = container
	end)
	if not success then
		warn("Flag object not built correctly. Please load fresh template to see how flag stand is expected to be made.")
	end
	
	BindBaseTouched(flagObject)
	DestroyFlag(flagObject)
	MakeFlag(flagObject)
	
	table.insert(FlagObjects, flagObject)
end

-- Event Bindings
--Players.PlayerAdded:Connect(OnPlayerAdded)

return FlagStandManager

Here’s the original, not working code

local FlagStandManager = {}

-- ROBLOX Services
local Players = game:GetService("Players")

-- Game Services
local Configurations = require(game.ServerStorage.Configurations)

-- Local Variables
local FlagObjects = {}
local FlagCarriers = {}
local Events = game.ReplicatedStorage.serverSidedRemotes
local CaptureFlag = Events.CaptureFlag
local ReturnFlag = Events.ReturnFlag

-- Local Functions
local MakeFlag

local function DestroyFlag(flagObject)
	flagObject.Flag:Destroy()
	for player, object in pairs(FlagCarriers) do
		if object == flagObject then
			FlagCarriers[player] = nil
		end
	end
end

local function OnCarrierDied(player)
	local flagObject = FlagCarriers[player]
	if flagObject then
		local flagPole = flagObject.FlagPole
		local flagBanner = flagObject.FlagBanner
		
		flagPole.CanCollide = false
		flagBanner.CanCollide = false
		flagPole.Anchored = true
		flagBanner.Anchored = true
		
		flagObject.PickedUp = false		
		
		FlagCarriers[player] = nil
		
		if Configurations.RETURN_FLAG_ON_DROP then
			wait(Configurations.FLAG_RESPAWN_TIME)
			if not flagObject.AtSpawn and not flagObject.PickedUp then
				DestroyFlag(flagObject)
				MakeFlag(flagObject)
				ReturnFlag:Fire(flagObject.FlagBanner.BrickColor)
			end
		end
	end
end

local function PickupFlag(player, flagObject)
	FlagCarriers[player] = flagObject
	flagObject.AtSpawn = false
	flagObject.PickedUp = true
	
	local torso
	if player.Character.Humanoid.RigType == Enum.HumanoidRigType.R6 then
		torso = player.Character:FindFirstChild('Torso')
	else
		torso = player.Character:FindFirstChild('UpperTorso')
	end
	local flagPole = flagObject.FlagPole
	local flagBanner = flagObject.FlagBanner
	
	flagPole.Anchored = false
	flagBanner.Anchored = false
	flagPole.CanCollide = false
	flagBanner.CanCollide = false
	local weld = Instance.new('Weld', flagPole)
	weld.Name = 'PlayerFlagWeld'
	weld.Part0 = flagPole
	weld.Part1 = torso
	weld.C0 = CFrame.new(0,0,-1)
end

local function BindFlagTouched(flagObject)
	local flagPole = flagObject.FlagPole
	local flagBanner = flagObject.FlagBanner
	flagPole.Touched:Connect(function(otherPart)
		local player = Players:GetPlayerFromCharacter(otherPart.Parent)
		if not player then return end
		if not player.Character then return end
		local humanoid = player.Character:FindFirstChild('Humanoid')
		if not humanoid then return end
		if humanoid.Health <= 0 then return end
		if flagBanner.BrickColor ~= player.TeamColor and not flagObject.PickedUp then
			PickupFlag(player, flagObject)
			Events.StoleFlag:Fire(player)
		elseif flagBanner.BrickColor == player.TeamColor and not flagObject.AtSpawn and Configurations.FLAG_RETURN_ON_TOUCH then
			DestroyFlag(flagObject)
			MakeFlag(flagObject)
			ReturnFlag:Fire(flagObject.FlagBanner.BrickColor)
		end
	end)
end

function MakeFlag(flagObject)
	flagObject.Flag = flagObject.FlagCopy:Clone()
	flagObject.Flag.Parent = flagObject.FlagContainer
	flagObject.FlagPole = flagObject.Flag.FlagPole
	flagObject.FlagBanner = flagObject.Flag.FlagBanner
	flagObject.FlagBanner.CanCollide = false
	flagObject.AtSpawn = true
	flagObject.PickedUp = false
	BindFlagTouched(flagObject)
end

local function BindBaseTouched(flagObject)
	local flagBase = flagObject.FlagBase
	flagBase.Touched:Connect(function(otherPart)
		local player = Players:GetPlayerFromCharacter(otherPart.Parent)
		if not player then return end
		if flagBase.BrickColor == player.TeamColor and FlagCarriers[player] then
			CaptureFlag:Fire(player)
			local otherFlag = FlagCarriers[player]
			DestroyFlag(otherFlag)
			MakeFlag(otherFlag)
		end
	end)
end

local function OnPlayerAdded(player)
	print("this should work.")
	player.CharacterAdded:Connect(function(character)
		print("this should work too.")
		character:WaitForChild('Humanoid').Died:Connect(function() OnCarrierDied(player) end)
	end)
	player.CharacterRemoving:Connect(function()
		print("this should work, three!")
		OnCarrierDied(player)
	end)
end

-- Public Functions
function FlagStandManager:Init(container)
	local flagObject = {}
	
	local success, message = pcall(function()
		flagObject.AtSpawn = true	
		flagObject.PickedUp = false
		flagObject.TeamColor = container.FlagStand.BrickColor
		flagObject.Flag = container.Flag
		flagObject.FlagPole = container.Flag.FlagPole
		flagObject.FlagBanner = container.Flag.FlagBanner
		flagObject.FlagBase = container.FlagStand
		flagObject.FlagCopy = container.Flag:Clone()	
		flagObject.FlagContainer = container
	end)
	if not success then
		warn("Flag object not built correctly. Please load fresh template to see how flag stand is expected to be made.")
	end
	
	BindBaseTouched(flagObject)
	DestroyFlag(flagObject)
	MakeFlag(flagObject)
	
	table.insert(FlagObjects, flagObject)
end

-- Event Bindings
Players.PlayerAdded:Connect(OnPlayerAdded)

return FlagStandManager

It’s not like ModuleScripts have this limitation.

I think the problem here is that when you require this script, the player has already joined. The connection is indeed functional. It’s just that the player had joined before it was created. PlayerAdded doesn’t account for that. It only accounts for new players, not pre-existing players.

You’d fix this issue with something like this:

-- This loop will account for any players who have already joined
for _, player in ipairs(Players:GetPlayers()) do
	task.spawn(onPlayerAdded, player)
end

-- Any new players will be handled as normal.
Players.PlayerAdded:Connect(onPlayerAdded)