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:
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