Any way to check if two scripts are the exact same at source level?

I am currently creating my own custom tool system and as I don’t want to compare the names (because this would limit my name choices eventually e.g. sword modules might have same name but slightly different behaviors) I would like to check if the module sources are the exact same.

I can’t compare sources because its a ProtectedString.

Logic:

local module_a = ModuleScript1
local module_b = ModuleScript2
local module_c = module_a:Clone()

if module_a is module_a then
-- would be true
end

if module_a is module_c then
-- would be true
end

if module_a is module_b then
-- would be false
end

Using == does not work obviously.

As such I could then program the behavior so that if they are the same this would be considered “unequipping” like a normal tool would when you hit the same button.

As far as I know there is no way to compare sources in games, but you can compare them with studio plugins

In plugins, you can check the .Source property, but this isn’t an option for runtime code. I would recommend putting a key inside of the module that states what source level it’s using. For example:

local module = {}

function module.whatever() -- whatever

end

module.level = 3 -- change this for different modules

return module
2 Likes

Well if all the modules return a table then you could compare all the indexes between to the two tables and see if they are the same, but it would be much better to just have some sort of identifier in every module to tell them apart as mentioned by @rogchamp

For functions, each module would have a different copy of functionally identical functions, which is why that wouldn’t be the best approach. Modules with similar behavior would probably have the same functions with slightly tweaked functionality.

2 Likes

that’s why i said the indexes not the values. For example:

local t = {}

function t:i()

end

local t2 = {}

function t2:i()
    
end

for i,v in pairs(t) do
    print(t2[i]) -- this will print the function
end 
1 Like

Yes, that’s why I mentioned this:

For example, a and b might both have a swing function, but you can’t compare to see if the functions are the same, you’d just see two modules with a function named swing.

3 Likes

Yeah I know that would be the pitfall, which is why your method is full proof.

1 Like

I saw on the .Source page about the whole plugins, going to go for another method instead which is a shame - wish it could be done by the server on runtime.

The only issue with your key approach is :Clone() and comparing the source, whereas you could do this to see if two created ones are the same module you’d be unable to do this to see if the module script is an exact replica.

Might make a feature request I guess.

If you didn’t use local variables within your functions (which you should since local variables are faster to access due to being stored closer up the stack) you could use some sort of redundant and extensive check

local function dumpGlobalConstants(f) 
	local function errorInfo() local trace = debug.traceback() return "\n\n error: passed value is not a function - function name: " ..trace:match("(.)") .." \n module line: " .. trace:match("%d+")    end
	assert(type(f) == "function", 
		errorInfo()
	)
	local env = {}
	setfenv(f, env) 
	do pcall(f) end
	return env
end

change the environment of the function and return all (globally-defined-within scope) variables and associated values, then compare them - this won’t necessarily guarantee you similarity for several reasons, but the odds are you aren’t using the exact same names and values for variables in your functions (don’t recommend this one since it forces you to access values slower, and isn’t too reliable).

I would recommend rogchamp’s method, just you’ll have to make sure manually they’re the same to later be able to rely on the source level.

Why would it not work with cloning? Surely the key’s value would exist in the clone as well as the original, indicating they are the same. Why would they be the same but not an “exact replica”? Whatever you are doing to one but not the other, the only way to make the modules not the exact same is to change the source in one of them, in which case you can also change the identifier.

I don’t think this needs a new feature, I think your system needs a bit of a rethink. If what you’re after isn’t possible and nobody else is doing it that way, it does raise the question of whether you’re going about it in the correct way. You might find there’s a much easier way to solve your problem that doesn’t rely on checking the contents of modules, as there shouldn’t be any time where you have two of the same module - it kind of defeats the point of a module if you have copies of them.

Whereas your solution is technically a solution, I was already aware that I could do your method and felt I’d ask if I can check it through the source itself.

Your system relies on me actually giving it a unique identifier which I already knew about and hence did not want to do. It even says on the module script api itself about a non-unique identifier so I was fully aware about identifiers. It would require me to require() the two modules to get the level which I don’t particularly want to do on my server side, when the module will then immediately be passed to a client side if they aren’t the same. Hence I’d rather have have a way of comparing if the source code was the same without having to require the module.

My case instance for the module is creating animated tools which are equipped to the character. The tool controls run on a chip system which allows for additional actions when changing out tools for example and gives more general control.

As I don’t want an exploiter being able to get the modules to control parts client side the server hands modules (with a local script running capturing mouse clicks to pass to the weapon controller as it’s an object oriented module). The server script is very secure, hence it’s a lot easier for it to quickly detect malformed arguments - it’s complicated to explain all of this quickly but the reality is it’s a case for it.

A lot of my project is being created with the intent of stopping exploitation from the client to the server (so other players see the effects). I only know to my knowledge around 2/3 games which use the system. The fact is I know people will try to exploit, and that my system is extremely secure in design - it’s just taking extra steps to limit the client that little bit more. I wouldn’t say I’m fully bothered about client side exploits, just as long as they don’t ruin the general game experience.

If you don’t see my reasoning as justification for a system as I stated then I apologise if my explanation is lacking.

I think I understand your use, however I still think there are easier ways to do this without needing to check that modules are the same. Ideally all the modules you’d need for all the tools would be available to the server from the start, and essentially lazy loading occurs where only the modules needed for each tool are loaded as they equip them.

I roughly understand what you’re trying to do, but there really shouldn’t be a need to clone a module, especially not if the modules are handled on the server. If you’re cloning things, then they have to have been stored somewhere to begin with, in which case they could simply be stored in a static location.

It’s sort of an XY problem here, of you needing a very specific feature to solve a very specific problem, when actually there’s likely easier ways to solve it if you take a few steps back and take another look at the system architecture.

Well the module is ran on the client and the tools are animated, hence due to clients being able to run animations on themselves I’ve decided it’s best that the server facilitates the control of the tool module they have loaded in to prevent them from running the animations. It’s just due to the variation of weapons - some people may have the same weapon whereas others might have different ones - as such they just get given copies of modules they require at the time.

That is unfortunately the one big downside to animated weaponry in that the animations automatically replicate over whereas with other tools they do not.

I see. In that case I’d have all the modules in ReplicatedStorage separate to the tool and just have a script or a single unique module in each tool that simply calls the relevant modules from ReplicatedStorage to prevent the modules being duplicated at all. That’s how I would elect to set it up. Modules don’t take much memory or anything so there’s no harm in having modules that don’t actually get used sat in ReplicatedStorage

How would you prevent the client from loading others themselves through your method?

The current method is quite a good preventive. I wouldn’t be particularly bothered by modules sat in the storage anyway - but its more I’d rather not have players using the tool modules when they please (whereas you can do little damage with a region module).

I wouldn’t - you can’t prevent the client from loading or running any code they want to if there is an exploiter. You just do anything important on the server at the client’s request, after passing it through validation such as how fast they are sending requests, whether the request matches what you expect, and handle range checks and whether or not the user could actually hit what they tell you they are hitting on the server, knowing what tool/weapon they claim to have equipped, and whether they are allowed to equip that particular weapon.

If the client loads other modules through exploit, then they can also disable any method you have of checking. If you were thinking of checking source on the client - they can bypass that. If you were able to check source on the server, it wouldn’t matter because the exploiter could create a new module or execute anything they want on the client and you won’t know about it on the server - you only know about stuff that the server itself has created and replicated to the client, which isn’t where the exploit will be.

Realistically you have to accept everything on the client may be compromised and anything they send to the server might be faked - that’s why on the server you check they are allowed the weapon they say they have - you do damage and range and other checks and actions on the server - and you never trust the client. If Roblox did implement source checking it wouldn’t change anything as you’d still need those same checks and you shouldn’t trust the client any more than you would without the feature.

Hence would it not be better to not give them a direct way of manipulating any tool they want with perfect arguments. In a sense if they require() the module in ReplicatedStorage as they’d have a whole list of them would this not be basically giving them free opportunity. Preferably I wanted the .Source method because then I could utilize the load the same module again and again as it could recognize they are the same and not have to hand a new one to the client to use.

Whereas I respect that exploiters can create their own modules, the probability they will script one from scratch is a lot lower then scripting one to interact with a pre-given module. The client is never being trusted, the server just trusts what it sees in the end - but technically a player can equip and use secondary tools whilst the server recognizes them as having the primary tool.