local c = game:GetService('ContextActionService')
print(c) -- ContextActionService
print(getmetatable(c)) -- The metatable is locked
c.u = 'u' -- u is not a valid member of ContextActionService "ContextActionService"
print(c[1]) -- 1 is not a valid member of ContextActionService "ContextActionService"
local function LockTable(Table)
local proxy = newproxy(true) -- creates blank userdata with metatable (the reason we use userdata is because if we used table, users would still be able to edit the table with the table library and rawget/rawset)
local meta = getmetatable(proxy) -- gets metatable of blank userdata
meta.__index = function(_, key)
return Table[key] -- return the value from the "real table"
end
meta.__newindex = function(_, key, value)
-- can leave the blank or throw an error, the point is that you don't set the value to the real table in order to have it "locked"
end
meta.__metatable = "locked" -- lock metatable
return proxy
end
local tab = LockTable({
key = "value"
})
-- tests
print(tab.key) --> "value"
tab.key = "anothervalue"
print(tab.key) --> "value"
print(getmetatable(tab)) --> "locked"
table.insert(tab,"randomvalue") --> error: must pass table got userdata
Protecting a table is possible through something called metamethods. Metamethods are methods that involve metatables, such as __index, __metatable, etcetra.
To “protect” a table from getmetatable, you would use the __metatable metamethod:
local x = {};
setmetatable(x, {
__metatable = "This table is locked.";
});
print(getmetatable(x)); -- This table is locked.
To prevent new entries from being added, you would use the __newindex metamethod:
local x = {};
setmetatable(x, {
__newindex = function(_table, _index, _value) print(_value, "is not a valid member of table."); return end;
});
x[1] = 2; -- 1 is not a valid member of table.
Combining the new yields:
local x = {};
setmetatable(x, {
__metatable = "This table is locked.",
__newindex = function(_table, _index, _value) print(_value, "is not a valid member of table."); return; end,
});
print(getmetatable(x)); -- This table is locked.
x[1] = 2; -- 1 is not a valid member of table.
You would still be able to edit existing keys in the table if you do that. The only way to do it is have a proxy table and use the __index metamethod to retrieve values from the “real table”.
Use the newly added table.freeze, it locks a table the same way Roblox would lock the string and math libraries. It’s better than using metamethods since they tend to be performance unfriendly.
EDIT: This doesn’t seem to be enabled. Also it doesn’t lock access to the metatable, you should still use __metatable for that