Best way to make a singleton client script for hold-click tools

a game i’m working on is heavily reliant on default roblox tools and i’m trying to make a system that handles tools using 1 server script and 1 local script (considering just cloning a local script into every tool though)

i stole some code from documentation as well as the fastcast example gun to make the script below but i have no idea how efficient this is

in general i’m just looking for the best way to handle mass amounts of tools that basically do the same thing

local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local mouse = nil
local mouseDown = false
local expectingInput = false

local tag = "Tool"

local connections = {}

local function OnInstanceAdded (instance)
	if instance:IsA("Tool") then
		connections[instance] = RunService.Stepped:Connect(function()
			if mouseDown then
				ReplicatedStorage.RemoteEvent:FireServer(instance)
			end
		end)
		
		connections[instance] = instance.Equipped:Connect(function(playerMouse)
			mouse = playerMouse
			mouseDown = false
			expectingInput = true
		end)
		
		connections[instance] = instance.Unequipped:Connect(function()
			mouseDown = false
			expectingInput = false
		end)
	end
end

local function OnInstanceRemoved (instance)
	if connections[instance] then
		connections[instance]:Disconnect()
		connections[instance] = nil
	end
end

CollectionService:GetInstanceAddedSignal(tag):Connect(OnInstanceAdded)
CollectionService:GetInstanceRemovedSignal(tag):Connect(OnInstanceRemoved)

for _, instance in pairs(CollectionService:GetTagged(tag)) do
	OnInstanceAdded(instance)
end

UserInputService.InputBegan:Connect(function (input, gpe)
	if gpe or not expectingInput then return end

	if input.UserInputType == Enum.UserInputType.MouseButton1 and mouse ~= nil then
		mouseDown = true
	end
end)

UserInputService.InputEnded:Connect(function (input, gpe)
	if gpe or not expectingInput then return end

	if input.UserInputType == Enum.UserInputType.MouseButton1 and mouse ~= nil then
		mouseDown = false
	end
end)
2 Likes

You could use a module script and make the scripts reference the module script or I think you can use TagService

2 Likes

2 questions:

isn’t TagService and CollectionService the same thing?

would a module look something like this:

function module:HandleClient (tool)
RemoteEvent:Fire(tool)
end

function module:HandleServer (tool)
-- Handle important things here
end

?

1 Like

I don’t use TagService much thus the reason why I said I think, and the module script should look like

Module Script

local module = {}

module.testFunction = function()
	-- Tool do something
end

return module

Regular script or local script

local tool = script.Parent
local getModuleScript = require(workspace.ModuleScript)

tool.Activated:Connect(function()
	getModuleScript.testFunction()
end)
2 Likes

As @iATtaCk_Noobsi said, you’re probably better off using module scripts. However, if you wanted to use a regular script, I recommend using OOP.

Example:

local tool_handler = {}

function tool_handler.new() --constructor
 local self = setmetatable({}, __index = tool_handler) --creating a new metatable
 self.registered_items = {}
 return self
end

function tool_handler:register_items(class)
 table.insert(self.registered_items, class.new()) --inserting the tool into the registered items table
end

function tool_handler:fire_tool(plr, index)
 local tool = self.registered_items[index] --finding the registered tool you wanted

 if tool then --checking if the tool exists
  tool:activate(plr)
 end
end

--//Tool class
local tool_class = {}

--create the constructor

--activate
function tool_class:activate(plr)
 --implement tool behavior
end

Basically, I created a tool handler class responsible for managing all the tools. The tool_class is a generic class that you can use as a template for all your tools. When a tool is activated, you need to call the fire tool function.

Hope this helps :cowboy_hat_face:

(P.S., I am not that great with OOP, and I’m still learning it. I had to use ChatGPT to help me with this, but I did learn quite a bit.)

2 Likes

i see, using a oop approach would be smart

so my idea is to get everything tagged and call register_items but i’m unsure if i should do this on the client or the server

i’m also using remotes events in my current setup, so i’m unsure how i would incorporate this

2 Likes

Depends on what the tool is. I would lean probably more towards the server. If you wanted some stuff to be client sided, just use remote events/functions.

2 Likes