--CLIENT
UserInputService.InputBegan:Connect(function(input, processed)
if not processed then
for slot, ability in pairs(abilities) do
local keybind = slotsToKeybinds[slot]
if input.KeyCode == keybind then
ability.Functions.Activate(ability)
activateRE:FireServer(ability.Index)
end
end
end
end)
--SERVER
activateRE.OnServerEvent:Connect(function(player, index)
local abilities = AbilityService.GetPlayerAbilities(player)
local ability = abilities[index]
if ability then
ability.Functions.Activate(ability)
end
end)
im calling the same activate function on both server and client (at the same time) and i was wondering whats the smartest way to go about separating them, so i can handle my vfx, animation, etc… on the client and then handle damage and blah blah blah on the server but im stuck and cant think of the right way to go about this
Well, I mean, try having two seperate module scripts? Also, exploiters can read and steal ModuleScripts if they’re not in a server-only objects (like ServerScriptStorage, ServerStorage) so you’ll probably want to do that anyways.
Also-also, you should fire the remote event before calling the ability on the client so it’s not delayed by any wait()s the function might have.
It doesn’t matter if they can change module scripts, if you validate everything correctly they will only ruin their gameplay (such as deleting VFX completely or changing it)
Theft? that’s your concern?.. Don’t want to be rude but no one is going to steal anything, there are far better games and open features to the client and those games don’t care.
It takes practically no effort to secure your game in that way, so there’s no reason not to do it.
Smaller games are also a little more at risk of theft since they have less of a reputation, allowing a stolen game to potentially surpass the original. This actually has happened before with Undead Nation, though I’ll admit that was way back when exploiters could also steal server scripts.
Ever heard o Miners Haven (or sum like that). The game is huge, but it’s literally open to studio.
Think of this situation like this. Let’s say you have a chess game, and you have a module script that calculates all legal moves based on Matrix (2D array). Now, the client can select a piece to preview all legal moves he can play, which requires for him to access that module script. But at the same time you don’t want to share your chess system. Would you rather have good experience playing or make everything server-sided?
Personally in the situation above, I wouldn’t care about the module being accessed or viewed by exploiters. I’d prefer a good experience while playing the game.
Also I gave you an example where you can make main module which is ran on server only server-sided.
The only thing you are exposing is VFX module, theres no other way if you want VFX client-sided. And no one really cares how you do VFX.
No, not really. I imagine they opened the game to studio after obtaining a significant reputation, not as soon as the game was open to be played.
I’m not saying everything must be server-sided, but if the client is never going to use something anyways, why allow it? It only makes the game easier to steal, and it’s very easy to secure it.
Nevermind you are not the OP, I thought you was the OP and it sounded like you were questioning about securing module scripts but then right away answering your own question
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Maid = require(ReplicatedStorage.Maid)
local RemoteEvents = ReplicatedStorage:WaitForChild("RemoteEvents")
local replicateEquipRE = RemoteEvents:WaitForChild("ReplicateEquipRE")
local replicateUnequipRE = RemoteEvents:WaitForChild("ReplicateUnequipRE")
local activateRE = RemoteEvents:WaitForChild("ActivateRE")
local players = {}
-- Load all abilities from the Abilities folder
local AbilitiesFolder = ReplicatedStorage:WaitForChild("Abilities")
local AbilityService = {
Abilities = {}
}
for _, moduleScript in pairs(AbilitiesFolder:GetChildren()) do
if moduleScript:IsA("ModuleScript") then
local ability = require(moduleScript)
AbilityService.Abilities[ability.Name] = ability
end
end
-- Get or create a player's abilities table
function AbilityService.GetPlayerAbilities(player)
local abilities = players[player] or {}
players[player] = abilities
return abilities
end
-- Equip an ability for a player
function AbilityService.EquipAbility(player, character, abilityName, index)
local abilities = AbilityService.GetPlayerAbilities(player)
local abilityInfo = AbilityService.Abilities[abilityName]
assert(abilityInfo, "Ability not found: " .. abilityName)
assert(not abilities[index], "Slot is already taken")
local ability = setmetatable({
Player = player,
Character = character,
Index = index,
Maid = Maid.new(),
Active = true,
}, { __index = abilityInfo })
abilities[index] = ability
abilityInfo.Functions.Equip(ability)
replicateEquipRE:FireClient(player, abilityName, index)
end
-- Unequip an ability for a player
function AbilityService.UnequipAbility(player, index)
local abilities = AbilityService.GetPlayerAbilities(player)
local ability = abilities[index]
if ability then
ability.Functions.Unequip(ability)
ability.Maid:Destroy()
abilities[index] = nil
replicateUnequipRE:FireClient(player, index)
end
end
-- Clear all abilities for a player
function AbilityService.ClearAbilities(player)
local abilities = AbilityService.GetPlayerAbilities(player)
for index in pairs(abilities) do
AbilityService.UnequipAbility(player, index)
end
end
-- Listen for ability activation requests from the client
activateRE.OnServerEvent:Connect(function(player, index)
local abilities = AbilityService.GetPlayerAbilities(player)
local ability = abilities[index]
if ability then
ability.Functions.ServerActivate(ability)
end
end)
-- Clean up abilities when a player leaves
Players.PlayerRemoving:Connect(function(player)
AbilityService.ClearAbilities(player)
end)
return AbilityService
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local Maid = require(ReplicatedStorage.Maid)
local player = Players.LocalPlayer
local RemoteEvents = ReplicatedStorage:WaitForChild("RemoteEvents")
local replicateEquipRE = RemoteEvents:WaitForChild("ReplicateEquipRE")
local replicateUnequipRE = RemoteEvents:WaitForChild("ReplicateUnequipRE")
local activateRE = RemoteEvents:WaitForChild("ActivateRE")
local abilities = {}
local slotsToKeybinds = {
[1] = Enum.KeyCode.One,
[2] = Enum.KeyCode.Two,
[3] = Enum.KeyCode.Three,
[4] = Enum.KeyCode.Four,
[5] = Enum.KeyCode.Five,
}
-- Load all abilities from the Abilities folder
local AbilitiesFolder = ReplicatedStorage:WaitForChild("Abilities")
local AbilityController = {
Abilities = {}
}
for _, moduleScript in pairs(AbilitiesFolder:GetChildren()) do
if moduleScript:IsA("ModuleScript") then
local ability = require(moduleScript)
AbilityController.Abilities[ability.Name] = ability
end
end
-- Equip an ability
function AbilityController.EquipAbility(abilityName, index)
local abilityInfo = AbilityController.Abilities[abilityName]
assert(abilityInfo, "Ability not found: " .. abilityName)
local ability = setmetatable({
Player = player,
Character = player.Character,
Index = index,
Maid = Maid.new(),
Active = true,
}, { __index = abilityInfo })
abilities[index] = ability
abilityInfo.Functions.Equip(ability)
end
-- Unequip an ability
function AbilityController.UnequipAbility(index)
local ability = abilities[index]
if ability then
ability.Functions.Unequip(ability)
ability.Maid:Destroy()
abilities[index] = nil
end
end
-- Listen for server events to equip/unequip abilities
replicateEquipRE.OnClientEvent:Connect(AbilityController.EquipAbility)
replicateUnequipRE.OnClientEvent:Connect(AbilityController.UnequipAbility)
-- Listen for player input to activate abilities
UserInputService.InputBegan:Connect(function(input, processed)
if not processed then
for slot, ability in pairs(abilities) do
local keybind = slotsToKeybinds[slot]
if input.KeyCode == keybind then
ability.Functions.ClientActivate(ability)
activateRE:FireServer(ability.Index)
end
end
end
end)
return AbilityController
if you mean to make the server activate function different from the client activate function then you can
1-seperate them into 2 module scripts 1 for client and 1 for server
2-use RunService:IsServer() to seperate the logic of 1 function (less recommended than 1 since exploiters will be able to see the server sided code of the function Activate)