So, say I have an OOP class, Car. This class will need to be used both server side and locally. If I put it in replicated storage then both client and server can access it, but it leaves an unnecessarily large amount of my code available to the client, and I’m worried about it being stolen. What is the best approach to this situation in your opinion? Am I worrying too much about my code? Should I attempt to split the class into modules, some that are only on the server?
Only expose necessary code to the client. I would leave serverside modules in ServerScriptSvc or ServerStorage.
Have a look for the pimpl (“pointer to implementation”) pattern. All the logic to the car is in a private object in ServerScriptService, then the public facing car has a pointer to the private object and redirects calls to it.
Basically, you’d have something like this (though there’s probably a better way to implement it but this shows the idea):
ServerScriptService CarPrivate.lua
local CarPrivate = { };
CarPrivate.new = function ()
local self = setmetatable({ }, {__index = CarPrivate});
return self;
end
CarPrivate.doMethod = function (self)
-- do something
end;
return CarPrivate;
ReplicatedStorage Car.lua
local CarPrivate = require(game:GetService("ServerScriptService").CarPrivate);
local Car = { };
Car.new = function (...)
local self = setmetatable({
__priv = CarPrivate.new(...);
}, {
__index = Car
});
return self;
end;
Car.doMethod = function (self)
return self.__priv:doMethod();
end;
return Car;
Edit: I’ve just had a thought… can scripts in replicated storage even require scripts in server script service?
Local scripts cannot
And server scripts don’t run in replicated storage.
So no…
Doh.
Ignore me then
Modules in ReplicatedStorage/ScriptService can require things in ServerStorage/ScriptService, as I was already running a rudimentary system similar to what @dr01d3k4 proposed for some of my code:
local isServer = game:GetService("RunService"):IsServer()
local module = {}
item = {}
item.Name = "Belt"
item.Model = game.ReplicatedStorage.Machines.Belt
if isServer then
item.Class = require(game.ServerStorage.Modules.Classes.Machine.Belt)
end
module[1] = item
You could save the module to ur profile and require(AssetID). This would prevent it being read
Can’t require private ModuleScripts on the client.
Gotcha
Personally, I wouldn’t worry about it being stolen, stopping people from stealing your code is a slippery slope. You can move the module from replicated storage to server script service and send it through an RE, but that doesn’t really stop a hacker because ultimately, anything on the client can be stolen (and for that matter, servers aren’t 100% secure). However, I’ve come up with this solution, I don’t know if it’s any good, but it seems like it serves the purpose.
Module -
local module = {}
module.__index = module
local var = "henloooo"
function module.new()
return setmetatable({double = "double"}, module)
end
function module:henlo()
print("henlo world")
print(var)
end
function module:changeVar()
var = "new"
end
return module
Server -
local event = game.ReplicatedStorage.RemoteEvent
local module = require(script.ModuleScript)
game.Players.PlayerAdded:connect(function(player)
wait(5)
event:FireClient(player, module)
module:changeVar()
module:henlo() -- prints new
end)
Client -
local event = game.ReplicatedStorage.RemoteEvent
event.OnClientEvent:connect(function(module)
local m = module.new()
m:henlo() -- prints henlooo
end)
Disclaimer: I am neither a security expert, nor an OOP expert, this is just a fun solution I came up with
If you’re need private variables with your OOP, I highly recommend Construct. As far as securely replicating Objects (I’m assuming you’re wanting to share the Car class on both server and client), I’d typically put everything I want to replicate under the ServerScript, require it first on the Server, then CLONE and put the cloned Instance in RepicatedStorage so there’s absolutely no way of accessing the server from that aspect. That doesn’t change how your code implementation works, but just assume the client is 100% after your code. You’re going to have to expose it, there’s nothing you can do about that. What you can do is have the server verify any actions the client sends to prevent spreading abuse and contain it to themselves.
I just have two different versions of the same class -
In your case, it would be a Car
class in serverstorage/serverscriptservice that is never exposed to the client.
Then there would be a ClientCar
class in replicatedstorage that is exposed to the client.
A ModuleScript in ReplicatedStorage can absolutely require a script in ServerScriptService or ServerStorage… provided it’s running on the server.
Script.Parent = nil
Please keep in mind that nothing on the client is safe, it is still accessible in memory so you have to let it go.
if i was in your shoes, I would go through the route of just splitting up the modules into a ClientCar and ServerCar module, and then just have a lot of boilerplate code…
if you want a more elegant solution, you could do some automation with some sort of OOP management system (variation of that construct thing people are talking about?) to create a centralized system of distributing client modules that is server-side driven
There’s no reason to prevent it being stolen. No matter what you do, it will still be able to be taken. You’re wasting valuable development time trying to keep your stuff safe and just making your code harder to work with in the long run.
I suggest you make sure that your gameplay is good, that way nobody would want to play a copy anyway. And usually, if you can prove yours was made first, you can have Roblox take down any stolen assets. That’s really the only good option.
Thanks everyone! After trying out a few suggestions, I have gone with having two different car classes, one available to the client and one private, as the best fit for the game.
(I did look at Construct, but still being very new to OOP and meta-tables I struggled to understand it)