Input handling and Cooldowns optimization in my framework

so in the past days i updated a bit my little framework that i asked tips about here : Simple Framework

(if i say something wrong then, my bad its not my main language. Italian moment)

i added :

  • Input handling
  • Cooldowns handling
  • some qol and other stuff

How does it work?

so its a shared framework that adapts to client and server while connecting them too making it easy to handle everything! here is an example of the client code as of now

its realy clean so i would like to keep this type of framework.

local Main = require(game:GetService("ReplicatedStorage"):WaitForChild("Modules"):WaitForChild("Shared"):WaitForChild("MainFramework"))

function TestKey()
	print("Hey")
end

Main.Init()
Main.Modules.Input.Init()

Main.Modules.Input:NewKeyboardInput("KeyTesting", Enum.KeyCode.E, "TestingCooldown", TestKey)

this is the first time i try to make a framework and i realy need some tips/tricks as i got no one to teach me.

Well i like it but i think it could be improved greatly so take a look and thanks in advice.

Explorer for reference:
image

MainFramework as for this i think its pretty good but its usefull to understand the rest of the code so ill leave it here anyway

local Main = {}

function Main.Init()
	--//Services\\--
	Main.ReplicatedStorage = game:GetService("ReplicatedStorage")
	Main.ServerStorage = game:GetService("ServerStorage")
	Main.ServerScriptService = game:GetService("ServerScriptService")
	Main.TweenService = game:GetService("TweenService")
	Main.UserInputService = game:GetService("UserInputService")
	Main.RunService = game:GetService("RunService")
	Main.Players = game:GetService("Players")
	
	--//Main variables\\--
	Main.EventsFolder = Main.ReplicatedStorage.Events
	Main.ModulesFolder = Main.ReplicatedStorage.Modules
	Main.CurrentCooldowns = {}
	Main.ModuleGroup = nil
	
	if Main.RunService:IsClient() then
		Main.Player = Main.Players.LocalPlayer
		Main.ModuleGroup = Main.ReplicatedStorage.Modules.Client
		
		print("Initialized Client MainFramework")
	elseif Main.RunService:IsServer() then
		Main.ModuleGroup = Main.ServerScriptService.MainServer
		
		print("Initialized Server MainFramework")
	end
	
	--//Events setup\\--
	Main.Events = {}
	
	for i,EventInstance in pairs(Main.EventsFolder:GetChildren()) do
		Main.Events[EventInstance.Name] = {}
			
		Main.Events[EventInstance.Name]["Name"] = EventInstance.Name
		Main.Events[EventInstance.Name]["Type"] = EventInstance.ClassName
		Main.Events[EventInstance.Name]["Connected"] = false
		Main.Events[EventInstance.Name]["TimesFired"] = 0
		Main.Events[EventInstance.Name]["Instance"] = EventInstance
	end
	
	--//Modules setup\\--
	Main.Modules = {}
	
	for i,ModuleInstance in pairs(Main.ModuleGroup:GetDescendants()) do
		if ModuleInstance:IsA("ModuleScript") then
			Main.Modules[ModuleInstance.Name] = require(ModuleInstance)
		end
	end
	
	for i,ModuleInstance in pairs(Main.ModulesFolder.Shared:GetDescendants()) do
		if ModuleInstance:IsA("ModuleScript") and ModuleInstance.Name ~= "MainFramework" then
			Main.Modules[ModuleInstance.Name] = require(ModuleInstance)
		end
	end
end

--//Events stuff\\--

function Main:ConnectEvent(Name, func)
	if Main.Events[Name] then
		Main.Events[Name].Connected = true
		
		--//Remote Events\\--
		if Main.Events[Name].Type == "RemoteEvent"  then
			if Main.RunService:IsClient() then
				Main.Events[Name].Instance.OnClientEvent:Connect(func)
				
			elseif Main.RunService:IsServer() then
				Main.Events[Name].Instance.OnServerEvent:Connect(func)
				
			end
			
		--//Remote Functions\\--
		elseif Main.Events[Name].Type == "RemoteFunction" then
			if Main.RunService:IsClient() then
				Main.Events[Name].Instance.OnClientInvoke = func

			elseif Main.RunService:IsServer() then
				Main.Events[Name].Instance.OnServerInvoke = func

			end
			
		--//Bindable Events\\--
		elseif Main.Events[Name].Type == "BindableEvent" then
			Main.Events[Name].Instance.Event:Connect(func)
		end
	end
end

function Main:FireEvent(Name, ...)
	if Main.Events[Name] then
		Main.Events[Name].TimesFired += 1
		
		--//Remote Events\\--
		if Main.Events[Name].Type == "RemoteEvent"  then
			if Main.RunService:IsClient() then
				Main.Events[Name].Instance:FireServer(...)
				
			elseif Main.RunService:IsServer() then
				Main.Events[Name].Instance:FireClient(...)
				
			end
			
		--//Remote Functions\\--
		elseif Main.Events[Name].Type == "RemoteFunction" then
			if Main.RunService:IsClient() then
				Main.Events[Name].Instance:InvokeServer(...)
				
			elseif Main.RunService:IsServer() then
				Main.Events[Name].Instance:InvokeClient(...)
				
			end
			
		--//Bindable Events\\--
		elseif Main.Events[Name].Type == "BindableEvent" then
			Main.Events[Name].Instance:Fire(...)
			
		end
	end
end

function Main:FireAllClients(Name, ...)
	if Main.Events[Name] then
		Main.Events[Name].TimesFired += 1
		
		if Main.Events[Name].Type == "RemoteEvent"  then
			if Main.RunService:IsServer() then
				Main.Events[Name].Instance:FireAllClients(...)
				
			end
		end
	end
end

--//Utility\\--

function Main.Tween(Target,Settings,Duration,Repeat,Yield)
	local info = TweenInfo.new(Duration,Enum.EasingStyle.Linear,Enum.EasingDirection.In,Repeat)
	local tweenObject = Main.TweenService:Create(Target, info, Settings)
	tweenObject:Play()

	if Yield then
		tweenObject.Completed:Wait()
	end
	return tweenObject
end

function Main.SetProperty(Target,Settings)
	for i,Setting in pairs(Settings) do
		Target[i] = Setting
	end
	wait()
end

function Main.roundNumber(num, numDecimalPlaces)
	return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num))
end

function Main.Timer(TimeVariable,Table)
	local Time = tick()
	repeat task.wait() until Main.roundNumber((tick() - Time),1) == Table[TimeVariable]

	Table[TimeVariable] = nil
end

return Main

Input handling (the one that i need most help on)

so here i think the for loop is a bit heavy and its just ugly to read maybe someone can help me optimize it

local Main = require(game:GetService("ReplicatedStorage"):WaitForChild("Modules"):WaitForChild("Shared"):WaitForChild("MainFramework"))
local Input = {}

function Input.Init()
	Main.UserInputService.InputBegan:Connect(function(Key,Processed)
		if Processed then return end 
		
		--//Main input cicle\\--
		
		for i,EventInput in pairs(Input) do
			if typeof(EventInput) == "table" then
				
				--//The EventInput is checked to see if its the right instance\\--
				if Key.KeyCode == EventInput.KeyCode then
					if EventInput.Cooldown then
						--//Check if there is a cooldown and relativly start the timer or just pass\\--
						
						local Check = Main.Modules.Cooldowns:CheckCooldown(Main.Player,EventInput.Cooldown)
						
						if not Check then
							Main.Modules.Cooldowns:AddCooldown(Main.Player,EventInput.Cooldown)
						else
							continue
						end 
					end
					EventInput.Instance:Fire()
				end
			end
		end
	end)
end

function Input:NewKeyboardInput(Name, Key, Cooldown, func)
	--//Setup a new Input object\\--
	
	Input[Name] = {}
	
	Input[Name].Name = Name
	Input[Name].KeyCode = Key
	Input[Name].Cooldown = Cooldown or false
	
	Input[Name].Instance = Instance.new("BindableEvent")
	Input[Name].Instance.Name = Name.."-Event"
	Input[Name].Instance.Parent = Main.EventsFolder
	
	Input[Name].Instance.Event:Connect(func)
end

return Input

Cooldowns for this im pretty concerned on how to handle the client-server interaction while making it hard to exploit but easy to work with like rn so in this ANY tip is accepted as i would make my life easier

local Main = require(game:GetService("ReplicatedStorage"):WaitForChild("Modules"):WaitForChild("Shared"):WaitForChild("MainFramework"))

local Cooldowns = {} 

--//List of cooldowns in seconds\\--
local CooldownList = {
	TestingCooldown = 2.5,
}

--//pretty self explanatory\\--
function Cooldowns:AddCooldown(Player, Cooldown)
	if not Main.CurrentCooldowns[Player.Name] then
		Main.CurrentCooldowns[Player.Name] = {}
	end

	Main.CurrentCooldowns[Player.Name][Cooldown] = CooldownList[Cooldown]
	local thread = coroutine.create(Main.Timer)

	coroutine.resume(thread, Cooldown, Main.CurrentCooldowns[Player.Name])
end

--//pretty self explanatory too\\--
function Cooldowns:CheckCooldown(Player, Cooldown)
	if not Main.CurrentCooldowns[Player.Name] then
		return false
	end

	return Main.CurrentCooldowns[Player.Name][Cooldown] ~= nil
end

return Cooldowns

i realy need some opinions so ill bump this up now, if you need more information tell me don’t just ignore my post pls

What specific problems do you have with your framework?

its a code review so got no problems, i just need some feedback and maybe some help with the cooldown part for client and server checks since idk if its the best