Last active
February 12, 2023 11:45
-
-
Save A1-exe/0c99361ca94050477728759b5eeb0d66 to your computer and use it in GitHub Desktop.
dank metatable memes. An api for hooking (straight up owning) any instance-related thing in the client of that one blocky game.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
Last Updated: 7/21/2021 12:25 AM | |
Last Change: Updated to include new synapse hookmetamethod | |
[ | |
- `__len` (# operator) returns raw object from wrapped objects | |
- #obj returns the unwrapped version of the object | |
- `__call` returns the __self table | |
Ex: __self = obj() | |
- `__self.caller` return the level of the calling function | |
- `__self.hook` returns the current hookfunction's table | |
- `__self.hookparent` returns the parent table of the hook | |
] | |
LOADING EXAMPLE: | |
[ | |
Loader Code | |
if not __hooks then | |
----- LOAD HOOK API ----- | |
local Script = game:HttpGet('https://gist.githubusercontent.com/A1-exe/0c99361ca94050477728759b5eeb0d66/raw/', true) | |
getgenv().__hooks = loadstring(Script)() | |
------------------------------- | |
end | |
] | |
--------------------------------------------------- | |
-- >> DOCUMENTATION << -- | |
--------------------------------------------------- | |
[ | |
** MOST IMPORTANT ** | |
** UNSET THE SAME WAY YOU USED SET | |
* Ex: __hooks.set(Inst, 'Prop', hookfunction) | |
* __hooks.unset(Inst, 'Prop') | |
* OR | |
* __hooks.set(Inst.Method, hookfunction) | |
* __hooks.unset(Inst.Method) | |
** NOTES ** | |
* One hook per property/function | |
* You can create a blank hook instead of using the setwrite function | |
* MethodHooks refer to any property/function that returns a function | |
(i.e. if it returns a function.. it's considered a method hook) | |
* If a hookfunction isn't passed to a regular hook... It returns the value, if the value isn't the same type, it returns the real value | |
* tostring hooking only works on instances | |
* `Name` property hooks automatically include tostring hooks | |
] | |
[ | |
Example Handler Args | |
** Regular Hook ** | |
* Inst <The Current Object> | |
* Value <The Real Property Value> | |
* newValue <Property Value Being Attempted> | |
* | |
Ex: handler(Inst, Value, newValue) | |
* Method Hook * | |
* Inst <The Current Object> | |
* realFunction <The Detected Function> | |
* ...*Args* <A tuple of passed arguments> | |
* | |
Ex: handler(Inst, detFunction, ...*Args*) | |
] | |
[ | |
Example Hook Table | |
{ | |
[1] = *function* < The hooking function >, | |
[2] = *boolean* < isHookDisabled >, | |
[3] = *boolean* < writeDisabled (Can this property be overwritten)) >, | |
[4] = *function* < a tracking function > | |
[5] = *function* < function before hookfunc > | |
[6] = *function* < function to hook for unset > | |
MainToggle = *boolean*, | |
MainWrite = *boolean* | |
} | |
Notes: | |
* MainToggle is used instead of `[2]` when no specific property is provided | |
* MainWrite is used instead of `[3]` when no specific property is provided | |
* ^ These are used to disable all hooks/writing to properties of one Inst/Class | |
] | |
(Raw Functions | Bypass Hooking API) | |
:: rawget | |
- Instance [Inst] | |
- Property [Str] | |
:: rawset | |
- Instance [Inst] | |
- Property [Str] | |
- Value [ALL] | |
:: rawcall | |
- Instance [Inst] | |
- Arguments [...*Args*] | |
- Method [Str] | |
(API Functions) | |
[ | |
Examples (get): | |
get(Instance) | |
] | |
:: get (Get a hook's table) | |
- Instance [Inst] | |
ClassName [Str] | |
Method [Func] | |
- Property [Str] (Optional) | |
[ | |
Examples (unset): | |
set(Instance, property) | |
set(Instance, {properties}) | |
set(ClassName, property) | |
set(ClassName, {properties}) | |
set(Function, hookfunction) | |
] | |
:: unset (Removes a hook) [ returns if a hook was removed ] | |
- Instance [Inst] | ClassName [Str] | Method [Func] | |
- Property [Str] | PropertyList [Array] | |
[ | |
Examples (set): | |
set(Instance, property, hookfunction) | |
set(Instance, {properties}, hookfunction) | |
set(ClassName, property, hookfunction) | |
set(ClassName, {properties}, hookfunction) | |
set(Function, hookfunction) | |
] | |
:: set (Create a hook) [ returns if a hook was created ] | |
- Instance [Inst] | ClassName [Str] | Method [Func] | |
- Property [Str] | PropertyList [Array] | MethodHook [Func] (Inst, Func, ...*Args*) | |
- HookFunction [Func] (Inst, Func, ...*Args*) | |
[ | |
Examples (toggle) | |
toggle(Instance, Property, boolean) | |
toggle(ClassName, Property, boolean) | |
toggle(Instance, boolean) // Toggles all properties | |
toggle(Classname, boolean) // Toggles all properties | |
] | |
:: toggle (Toggle hooks) [ returns if a hook was toggled ] | |
- Instance [Inst] | |
ClassName [Str] | |
Method [Func] | |
- Property [Str] | |
PropertyList [Array] | |
- Debounce [Bool] | |
[ | |
Examples (setwrite) | |
setwrite(Instance, Property, boolean) | |
setwrite(ClassName, Property, boolean) | |
setwrite(Instance, boolean) // Toggles all properties | |
setwrite(Classname, boolean) // Toggles all properties | |
] | |
:: setwrite (Toggle writing abilities) [ returns if a hook was modified ] | |
- Instance [Inst] | |
ClassName [Str] | |
- Property [Str] | |
PropertyList [Array] | |
- Debounce [Bool] | |
[ | |
Examples (showtostring) | |
showtostring(Instance, string) | |
] | |
:: showtostring (Mask tostring) [ returns if a hook was created ] | |
[ | |
Examples (hidetostring) | |
hidetostring(Instance) | |
] | |
:: hidetostring (Unmask tostring) [ returns if a hook was removed ] | |
----------------------------------------------- | |
---- >>> END <<< ---- | |
----------------------------------------------- | |
]] | |
local debugmode = false | |
local newcclosure = newcclosure or (function(f) return f end) | |
local getnamecallmethod = getnamecallmethod or get_namecall_method | |
local getrawmetatable = (hookmetamethod and (function() return nil end)) or getrawmetatable or debug.getrawmetatable | |
local setreadonly = function(t, b) | |
if setreadonly then | |
setreadonly(t, b) | |
elseif make_writable then | |
make_writable(t, not b) | |
elseif fullaccess then | |
fullaccess(t, not b) | |
end | |
end | |
if not getrawmetatable then | |
return "Methods not available." | |
end | |
local Metatable = getrawmetatable(game) | |
local MetatableIndex = Metatable and Metatable.__index | |
local MetatableCall = Metatable and Metatable.__namecall | |
local MetatableNew = Metatable and Metatable.__newindex | |
local MetatableTo = Metatable and Metatable.__tostring | |
local IsA = game.IsA | |
if debugmode then | |
print("LOADING:", (Metatable and "Is not") or "Is", "expiremental.") | |
print("INDEX:", MetatableIndex) | |
print("CALL:", MetatableCall) | |
print("NEW:", MetatableNew) | |
print("TO:", MetatableTo) | |
end | |
local hookfunc = hookfunc or hookfunction | |
local hooks, Hooks = {}, | |
{ | |
ToStrings = {}, | |
Globals = {}, | |
Funcs = {}, | |
Insts = {} | |
} | |
-- Relay types to table names | |
local function Get(i) | |
local Type = typeof(i) | |
return (((Type == "string") and "Globals") or ((Type == "function") and "Funcs") or | |
((Type == "Instance") and "Insts")) | |
end | |
-- Expand arguments to tables and wrap hookfunctions in CClosures | |
-- Return the last table and it's parent and index so it can be unset | |
local function Expand(t, ...) | |
local Args, Ret, RetParent, RetIndex = {...} | |
local Recurse | |
do | |
function Recurse(r, i) | |
if not Args[i] then | |
return Ret, RetParent, RetIndex | |
end | |
if not r[Args[i]] then | |
r[Args[i]] = | |
setmetatable( | |
{}, | |
{ | |
__newindex = function(s, i, v) | |
if (i == 1) and (typeof(v) == "function") then | |
rawset(s, i, newcclosure(v)) | |
else | |
rawset(s, i, v) | |
end | |
end | |
} | |
) | |
end | |
RetParent = r | |
RetIndex = Args[i] | |
Ret = r[Args[i]] | |
return Recurse(Ret, i + 1) | |
end | |
end | |
return Recurse(t, 1) | |
end | |
-- Wrap instances to prevent infinite loop | |
local WrapRaw; | |
WrapRaw = newcclosure(function(obj, func_self) | |
local newobj = newproxy(true) | |
local newmeta = getmetatable(newobj) | |
if (typeof(obj) ~= 'Instance') then | |
error('invalid argument #1 (Instance expected, got '..typeof(obj)) | |
end | |
-- // Use `__len` before in comparisons | |
newmeta.__len = newcclosure(function(self) | |
return obj | |
end) | |
newmeta.__call = newcclosure(function(self, ...) | |
return func_self | |
end) | |
newmeta.__tostring = newcclosure(function() | |
return tostring(obj) | |
end) | |
newmeta.__newindex = newcclosure(function(self, index, value) | |
hooks.rawset(obj, index, value) | |
end) | |
newmeta.__index = newcclosure(function(self, index) | |
local ret = hooks.rawget(obj, index) | |
if (typeof(ret) == 'Instance') then | |
return WrapRaw(ret, func_self) | |
end | |
if (typeof(ret) == 'function') then | |
return function(arg1, ...) | |
if (arg1 == self) then | |
return ret(obj, ...) | |
else | |
return ret(obj, arg1, ...) | |
end | |
end | |
end | |
return ret | |
end) | |
return newobj | |
end) | |
-- Add self to function | |
local function MakeSelf(tab, par, caller) | |
return { | |
caller = caller, | |
hookparent = par, | |
hook = tab, | |
} | |
end | |
---------------------------------------- | |
--------------- HOOK API --------------- | |
---------------------------------------- | |
hooks.get = | |
newcclosure( | |
function(Index, Property) | |
local Act = Get(Index) | |
if not Act then | |
return | |
end | |
local Target = Hooks[Act] | |
if Act then | |
return Expand(Target, Index, Property) | |
end | |
end | |
) | |
hooks.unset = | |
newcclosure( | |
function(Index, Props) | |
local Action, Act = typeof(Props), Get(Index) | |
if not Act then | |
return | |
end | |
if ((Act == "Globals") or (Act == "Insts")) and (Action == "table") then | |
for __, Property in next, (Props) do | |
local HookTable, HookTableParent, HookTableIndex = hooks.get(Index, Property) | |
if HookTable[5] and HookTable[6] then | |
hookfunc(HookTable[6], HookTable[5]) | |
end | |
HookTableParent[HookTableIndex] = nil | |
end | |
return true | |
elseif ((Act == "Globals") or (Act == "Insts")) and (Action == "string") then | |
local HookTable, HookTableParent, HookTableIndex = hooks.get(Index, Props) | |
if HookTable[5] and HookTable[6] then | |
hookfunc(HookTable[6], HookTable[5]) | |
end | |
HookTableParent[HookTableIndex] = nil | |
return true | |
elseif (Act == "Funcs") and (Action == "function") then | |
local HookTable, HookTableParent, HookTableIndex = hooks.get(Index) | |
if HookTable[5] and HookTable[6] then | |
hookfunc(HookTable[6], HookTable[5]) | |
end | |
HookTableParent[HookTableIndex] = nil | |
return true | |
end | |
end | |
) | |
hooks.set = | |
newcclosure( | |
function(Index, Props, Func) | |
local Action, Act = typeof(Props), Get(Index) | |
if not Act then | |
return | |
end | |
if ((Act == "Globals") or (Act == "Insts")) and (Action == "table") then | |
for __, Property in next, (Props) do | |
local HookTable = hooks.get(Index, Property) | |
HookTable[1] = (function() | |
if typeof(Func) == "function" then | |
if (Act == "Insts") and (Property == "Name") then | |
hooks.showtostring(Index, Func()) | |
end | |
return Func | |
else | |
if (Act == "Insts") and (Property == "Name") then | |
hooks.showtostring(Index, Func) | |
end | |
return function() | |
return Func | |
end | |
end | |
end)() | |
end | |
return true | |
elseif ((Act == "Globals") or (Act == "Insts")) and (Action == "string") then | |
local HookTable = hooks.get(Index, Props) | |
HookTable[1] = (function() | |
if typeof(Func) == "function" then | |
if (Act == "Insts") and (Props == "Name") then | |
hooks.showtostring(Index, Func()) | |
end | |
return Func | |
else | |
if (Act == "Insts") and (Props == "Name") then | |
hooks.showtostring(Index, Func) | |
end | |
return function() | |
return Func | |
end | |
end | |
end)() | |
return true | |
elseif (Act == "Funcs") and (Action == "function") then | |
-- Prevent cyclical `Instance[<method>]` bafoonery when indexing hooked functions. | |
-- Returned address for indexed funcitons change once | |
for hookedfunc, hookedtab in next, (Hooks.Funcs) do | |
if (hookedfunc == Index) or (hookedtab[4] == Index) then | |
Index = hookedfunc | |
break | |
end | |
end | |
local HookTable = hooks.get(Index) | |
HookTable[1] = Props | |
return true | |
end | |
end | |
) | |
hooks.toggle = | |
newcclosure( | |
function(Index, ...) | |
local Args, Act = {...}, Get(Index) | |
local Action = typeof(Args[1]) | |
if not Act then | |
return | |
end | |
if ((Act == "Globals") or (Act == "Insts")) and (#Args > 1) and (Action == "table") then | |
for __, Property in next, (Args[1]) do | |
local HookTable = hooks.get(Index, Property) | |
HookTable[2] = not Args[2] | |
end | |
return true | |
elseif (typeof(Act) == "string") and (#Args == 1) then | |
local HookTable = hooks.get(Index) | |
HookTable.MainToggle = not Args[1] | |
return true | |
elseif (Action == "string") and (#Args > 1) then | |
local HookTable = hooks.get(Index, Args[1]) | |
HookTable[2] = not Args[2] | |
return true | |
end | |
end | |
) | |
hooks.rawget = | |
newcclosure( | |
function(Obj, Index) | |
local Return = MetatableIndex(Obj, Index) | |
if (type(Return) == "function") then | |
local FuncHook = Hooks.Funcs[Return] | |
if FuncHook and FuncHook[5] then | |
return FuncHook[5] | |
end | |
local InstHook = Hooks.Insts[Obj] | |
if InstHook and not InstHook.MainToggle then | |
local Prop = InstHook[Index] | |
if Prop and Prop[5] then | |
return Prop[5] | |
end | |
end | |
for Class, Hook in next, (Hooks.Globals) do | |
if IsA(Obj, Class) and not Hook.MainToggle then | |
local ClassHook = Hook[Index] | |
if ClassHook and ClassHook[5] then | |
return ClassHook[5] | |
end | |
end | |
end | |
end | |
return Return | |
end | |
) | |
hooks.rawset = | |
newcclosure( | |
function(...) | |
MetatableNew(...) | |
end | |
) | |
hooks.rawcall = | |
newcclosure( | |
function(...) | |
return MetatableCall(...) | |
end | |
) | |
hooks.setwrite = | |
newcclosure( | |
function(Index, ...) | |
local Args, Act = {...}, Get(Index) | |
local Action = typeof(Args[1]) | |
if not Act then | |
return | |
end | |
if ((Act == "Globals") or (Act == "Insts")) and (#Args > 1) and (Action == "table") then | |
for __, Property in next, (Args[1]) do | |
local HookTable = hooks.get(Index, Property) | |
HookTable[3] = not Args[2] | |
end | |
return true | |
elseif (typeof(Act) == "string") and (#Args == 1) then | |
local HookTable = hooks.get(Index) | |
HookTable.MainWrite = not Args[1] | |
return true | |
elseif (Action == "string") and (#Args > 1) then | |
local HookTable = hooks.get(Index, Args[1]) | |
HookTable[3] = not Args[2] | |
return true | |
end | |
end | |
) | |
hooks.showtostring = | |
newcclosure( | |
function(Obj, Value) | |
if (typeof(Obj) == "Instance") and (typeof(Value) == "string") then | |
Hooks.ToStrings[Obj] = Value | |
end | |
end | |
) | |
hooks.hidetostring = | |
newcclosure( | |
function(Obj) | |
local StrHook = Hooks.ToStrings[Obj] | |
if StrHook then | |
Hooks.ToStrings[Obj] = nil | |
end | |
end | |
) | |
--------------------------------------------- | |
--------------- HOOK FUNCTIONS -------------- | |
--------------------------------------------- | |
---- MethodHook | |
local function PropertyHook(Return, Obj, Index) | |
local IsMethod = (type(Return) == "function") | |
if (Index == "MainToggle") or (Index == "MainWrite") then | |
return Return | |
end | |
local FuncHook = IsMethod and Hooks.Funcs[Return] | |
if FuncHook and FuncHook[1] then | |
if not FuncHook[4] then | |
local newFunc, oldFunc = function(obj, ...) | |
return FuncHook[1](WrapRaw(obj, MakeSelf(FuncHook, Hooks.Funcs, 4)), FuncHook[5], ...) | |
end | |
oldFunc = hookfunc(Return, newFunc) | |
FuncHook[4] = newFunc | |
rawset(FuncHook, 5, oldFunc) | |
rawset(FuncHook, 6, Return) | |
end | |
if not FuncHook[2] then | |
return FuncHook[4] | |
else | |
return FuncHook[5] | |
end | |
end | |
local InstHook = Hooks.Insts[Obj] | |
if InstHook and not InstHook.MainToggle then | |
local Prop = InstHook[Index] | |
if Prop and Prop[1] then | |
local __self = MakeSelf(Prop, InstHook, 5) | |
if IsMethod then | |
if not Prop[4] then | |
local newFunc, oldFunc = function(obj, ...) | |
__self.caller = 4 | |
return Prop[1](WrapRaw(obj, __self), Prop[5], ...) | |
end | |
oldFunc = hookfunc(Return, newFunc) | |
Prop[4] = newFunc | |
rawset(Prop, 5, oldFunc) | |
rawset(Prop, 6, Return) | |
end | |
return ((not Prop[2]) and Prop[4]) or Prop[5] | |
end | |
if not Prop[2] then | |
local mask = Prop[1](WrapRaw(Obj, __self), Return) | |
if (typeof(mask) == typeof(Return)) then | |
return mask | |
end | |
end | |
return Return | |
end | |
end | |
for Class, Hook in next, (Hooks.Globals) do | |
if IsA(Obj, Class) and not Hook.MainToggle then | |
local ClassHook = Hook[Index] | |
if ClassHook and ClassHook[1] then | |
local __self = MakeSelf(ClassHook, Hook, 5) | |
if IsMethod then | |
if not ClassHook[4] then | |
local newFunc, oldFunc = function(obj, ...) | |
__self.caller = 4 | |
return ClassHook[1](WrapRaw(obj, __self), ClassHook[5], ...) | |
end | |
oldFunc = hookfunc(Return, newFunc) | |
ClassHook[4] = newFunc | |
rawset(ClassHook, 5, oldFunc) | |
rawset(ClassHook, 6, Return) | |
end | |
return ((not ClassHook[2]) and ClassHook[4]) or ClassHook[5] | |
end | |
if not ClassHook[2] then | |
local mask = ClassHook[1](WrapRaw(Obj, __self), Return) | |
if (typeof(mask) == typeof(Return)) then | |
return mask | |
end | |
end | |
return Return | |
end | |
end | |
end | |
return Return | |
end | |
---- MethodHook | |
local function MethodHook(Obj, ...) | |
local Method = getnamecallmethod() | |
local MethodFunc = MetatableIndex(Obj, Method) | |
if (Method == "MainToggle") or (Method == "MainWrite") then | |
return MetatableCall(Obj, ...) | |
end | |
local FuncHook = Hooks.Funcs[MethodFunc] | |
if FuncHook and FuncHook[1] then | |
if not FuncHook[4] then | |
local newFunc, oldFunc = function(obj, ...) | |
return FuncHook[1](WrapRaw(Obj, MakeSelf(FuncHook, Hooks.Funcs, 4)), FuncHook[5], ...) | |
end | |
oldFunc = hookfunc(MethodFunc, newFunc) | |
FuncHook[4] = newFunc | |
rawset(FuncHook, 5, oldFunc) | |
rawset(FuncHook, 6, MethodFunc) | |
end | |
if not FuncHook[2] then | |
return FuncHook[1](WrapRaw(Obj, MakeSelf(FuncHook, Hooks.Funcs, 4)), FuncHook[5], ...) | |
else | |
return FuncHook[5](Obj, ...) | |
end | |
end | |
local InstHook = Hooks.Insts[Obj] | |
if InstHook and not InstHook.MainToggle then | |
local Prop = InstHook[Method] | |
if Prop and Prop[1] then | |
if not Prop[4] then | |
local newFunc, oldFunc = function(obj, ...) | |
return Prop[1](WrapRaw(Obj, MakeSelf(Prop, Hooks.Funcs, 4)), Prop[5], ...) | |
end | |
oldFunc = hookfunc(MethodFunc, newFunc) | |
Prop[4] = newFunc | |
rawset(Prop, 5, oldFunc) | |
rawset(Prop, 6, MethodFunc) | |
end | |
if not Prop[2] then | |
return Prop[1](WrapRaw(Obj, MakeSelf(Prop, Hooks.Funcs, 4)), Prop[5], ...) | |
else | |
return Prop[5](Obj, ...) | |
end | |
end | |
end | |
for Class, Hook in next, (Hooks.Globals) do | |
if IsA(Obj, Class) and not Hook.MainToggle then | |
local ClassHook = Hook[Method] | |
if ClassHook and ClassHook[1] then | |
if not ClassHook[4] then | |
local newFunc, oldFunc = function(obj, ...) | |
return ClassHook[1](WrapRaw(Obj, MakeSelf(ClassHook, Hooks.Funcs, 4)), ClassHook[5], ...) | |
end | |
oldFunc = hookfunc(MethodFunc, newFunc) | |
ClassHook[4] = newFunc | |
rawset(ClassHook, 5, oldFunc) | |
rawset(ClassHook, 6, MethodFunc) | |
end | |
if not ClassHook[2] then | |
return ClassHook[1](WrapRaw(Obj, MakeSelf(ClassHook, Hooks.Funcs, 4)), ClassHook[5], ...) | |
else | |
return ClassHook[5](Obj, ...) | |
end | |
end | |
end | |
end | |
return MetatableCall(Obj, ...) | |
end | |
---- StringHook | |
local function StringHook(Obj) | |
local StrHook = Hooks.ToStrings[Obj] | |
if StrHook then | |
return StrHook | |
end | |
return MetatableTo(Obj) | |
end | |
------------------------------------------------------ | |
--------------- ATTACH METATABLE HOOKS --------------- | |
------------------------------------------------------ | |
if Metatable then | |
setreadonly(Metatable, false) | |
end | |
----- NAMECALL ----- | |
local TrueNameCallHook = (MethodHook) | |
if not Metatable then | |
MetatableCall = hookmetamethod(game, "__namecall", TrueNameCallHook) | |
else | |
Metatable.__namecall = newcclosure(TrueNameCallHook) | |
end | |
----- TOSTRING ----- | |
local TrueToStringHook = (StringHook) | |
if not Metatable then | |
MetatableTo = hookmetamethod(game, "__tostring", TrueToStringHook) | |
else | |
Metatable.__tostring = newcclosure(TrueToStringHook) | |
end | |
----- INDEX ----- | |
local TrueIndexHook = ( | |
function(Parent, Index) | |
local Return = MetatableIndex(Parent, Index) | |
return PropertyHook(Return, Parent, Index) | |
end | |
) | |
if not Metatable then | |
MetatableIndex = hookmetamethod(game, "__index", TrueIndexHook) | |
else | |
Metatable.__index = newcclosure(TrueIndexHook) | |
end | |
----- NEWINDEX ----- | |
local TrueNewIndexHook = ( | |
function(Obj, Index, Value) | |
local Success, Return = pcall(function() | |
return MetatableIndex(Obj, Index) | |
end) | |
if not Success or (typeof(Return) ~= typeof(Value)) then | |
return MetatableNew(Obj, Index, Value) | |
end | |
local IsMethod = (typeof(Return) == 'function') | |
local InstHook = Hooks.Insts[Obj] | |
if InstHook then | |
local Prop = InstHook[Index] | |
-- If hook exists call it instead | |
if Prop and (typeof(Return) == typeof(Value)) and Prop[1] and not Prop[2] then | |
Prop[1](WrapRaw(Obj, MakeSelf(Prop, InstHook, 4)), (IsMethod and Prop[5]) or Return, Value) | |
return | |
end | |
if InstHook.MainWrite or (Prop and Prop[3]) then | |
return | |
end | |
end | |
for Class, Hook in next, (Hooks.Globals) do | |
if IsA(Obj, Class) then | |
local ClassHook = Hook[Index] | |
-- If hook exists call it instead | |
if ClassHook and (typeof(Return) == typeof(Value)) and ClassHook[1] and not ClassHook[2] then | |
ClassHook[1](WrapRaw(Obj, MakeSelf(ClassHook, Hook, 4)), (IsMethod and ClassHook[5]) or Return, Value) | |
return | |
end | |
if Hook.MainWrite or (ClassHook and ClassHook[3]) then | |
return | |
end | |
end | |
end | |
return MetatableNew(Obj, Index, Value) | |
end | |
) | |
if not Metatable then | |
MetatableNew = hookmetamethod(game, "__newindex", TrueNewIndexHook) | |
else | |
Metatable.__newindex = newcclosure(TrueNewIndexHook) | |
end | |
--------------------------------------- | |
--------------- CLEANUP --------------- | |
--------------------------------------- | |
if debugmode then | |
print("LOADED.") | |
print("INDEX:", MetatableIndex) | |
print("CALL:", MetatableCall) | |
print("NEW:", MetatableNew) | |
print("TO:", MetatableTo) | |
end | |
if Metatable then | |
setreadonly(Metatable, true) | |
end | |
if debugmode then | |
getgenv().__hooks = hooks | |
end | |
return hooks |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment