1555 lines
36 KiB
Lua
1555 lines
36 KiB
Lua
|
|
-- Copyright (C) 2017-2020 DBotThePony
|
|
|
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
-- of this software and associated documentation files (the "Software"), to deal
|
|
-- in the Software without restriction, including without limitation the rights
|
|
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
-- of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
-- subject to the following conditions:
|
|
|
|
-- The above copyright notice and this permission notice shall be included in all copies
|
|
-- or substantial portions of the Software.
|
|
|
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
-- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
-- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
-- DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
-- performance and functionality to the core
|
|
|
|
jit.on()
|
|
|
|
local pairs = pairs
|
|
local ipairs = ipairs
|
|
local print = print
|
|
local debug = debug
|
|
local tostring = tostring
|
|
local tonumber = tonumber
|
|
local type = type
|
|
local traceback = debug.traceback
|
|
local DLib = DLib
|
|
local unpack = unpack
|
|
local gxpcall = xpcall
|
|
local SysTime = SysTime
|
|
|
|
DLib.hook = DLib.hook or {}
|
|
local ghook = _G.hook
|
|
local hook = DLib.hook
|
|
|
|
hook.PROFILING = false
|
|
hook.PROFILING_RESULTS_EXISTS = false
|
|
hook.PROFILE_STARTED = 0
|
|
hook.PROFILE_ENDS = 0
|
|
|
|
hook.__tableOptimized = hook.__tableOptimized or {}
|
|
hook.__table = hook.__table or {}
|
|
hook.__tableGmod = hook.__tableGmod or {}
|
|
hook.__tableModifiersPost = hook.__tableModifiersPost or {}
|
|
hook.__tableModifiersPostOptimized = hook.__tableModifiersPostOptimized or {}
|
|
hook.__disabled = hook.__disabled or {}
|
|
|
|
local __table = hook.__table
|
|
local __disabled = hook.__disabled
|
|
local __tableOptimized = hook.__tableOptimized
|
|
local __tableGmod = hook.__tableGmod
|
|
local __tableModifiersPost = hook.__tableModifiersPost
|
|
local __tableModifiersPostOptimized = hook.__tableModifiersPostOptimized
|
|
|
|
-- ULib compatibility
|
|
-- ugh
|
|
_G.HOOK_MONITOR_HIGH = -2
|
|
_G.HOOK_HIGH = -1
|
|
_G.HOOK_NORMAL = 0
|
|
_G.HOOK_LOW = 1
|
|
_G.HOOK_MONITOR_LOW = 2
|
|
|
|
local maximalPriority = -10
|
|
local minimalPriority = 10
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.GetTable
|
|
@replaces
|
|
@returns
|
|
table: `table<string, table<string, function>>`
|
|
]]
|
|
local function GetTable()
|
|
return __tableGmod
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.GetDLibOptimizedTable
|
|
@returns
|
|
table: of `eventName -> array of functions`
|
|
]]
|
|
function hook.GetDLibOptimizedTable()
|
|
return __tableOptimized
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.GetDLibModifiers
|
|
@returns
|
|
table
|
|
]]
|
|
function hook.GetDLibModifiers()
|
|
return __tableModifiersPost
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.GetDLibSortedTable
|
|
@returns
|
|
table
|
|
]]
|
|
function hook.GetDLibSortedTable()
|
|
return __tableOptimized
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@replaces
|
|
@fname hook.GetULibTable
|
|
|
|
@desc
|
|
For mods like DarkRP
|
|
Althrough, DarkRP can use DLib's Post Modifiers instead for things that
|
|
DarkRP currently do with table provided by `GetULibTable`
|
|
@enddesc
|
|
|
|
@returns
|
|
table
|
|
]]
|
|
function hook.GetULibTable()
|
|
return __table
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.GetDLibTable
|
|
@returns
|
|
table
|
|
]]
|
|
function hook.GetDLibTable()
|
|
return __table
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.GetDisabledHooks
|
|
@returns
|
|
table
|
|
]]
|
|
function hook.GetDisabledHooks()
|
|
return __disabled
|
|
end
|
|
|
|
local oldHooks
|
|
|
|
if ghook ~= DLib.ghook then
|
|
if ghook.GetULibTable then
|
|
oldHooks = ghook.GetULibTable()
|
|
else
|
|
oldHooks = {}
|
|
|
|
for event, eventData in pairs(ghook.GetTable()) do
|
|
oldHooks[event] = {}
|
|
oldHooks[event][0] = {}
|
|
local target = oldHooks[event][0]
|
|
|
|
for hookID, hookFunc in pairs(eventData) do
|
|
target[hookID] = {fn = hookFunc}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function transformStringID(stringID, event)
|
|
if type(stringID) == 'number' then
|
|
stringID = tostring(stringID)
|
|
end
|
|
|
|
if type(stringID) == 'boolean' then
|
|
error('booleans are not allowed as hookID value', 3)
|
|
end
|
|
|
|
if type(stringID) ~= 'string' then
|
|
local success = pcall(function()
|
|
stringID.IsValid(stringID)
|
|
end)
|
|
|
|
if not success then
|
|
--if DLib.DEBUG_MODE:GetBool() then
|
|
--DLib.Message(traceback('hook.Add - hook ID is not a string and not a valid object! Using tostring() instead. ' .. type(stringID)))
|
|
error('hook.Add - hook ID is not a string and not a valid object! ' .. type(stringID), 3)
|
|
--end
|
|
stringID = tostring(stringID)
|
|
end
|
|
end
|
|
|
|
return stringID
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.DisableHook
|
|
@args string event, any hookID
|
|
|
|
@returns
|
|
boolean
|
|
]]
|
|
function hook.DisableHook(event, stringID)
|
|
assert(type(event) == 'string', 'hook.DisableHook - event is not a string! ' .. type(event))
|
|
|
|
if not stringID then
|
|
if __disabled[event] then
|
|
return false
|
|
end
|
|
|
|
__disabled[event] = true
|
|
return true
|
|
else
|
|
if not __table[event] then return false end
|
|
stringID = transformStringID(stringID, event)
|
|
|
|
for priority = -10, 10 do
|
|
if __table[event][priority] and __table[event][priority][stringID] then
|
|
local wasDisabled = __table[event][priority][stringID].disabled
|
|
__table[event][priority][stringID].disabled = true
|
|
hook.Reconstruct(event)
|
|
return not wasDisabled
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.DisableAllHooksExcept
|
|
@args string event, any hookID
|
|
|
|
@returns
|
|
boolean
|
|
]]
|
|
function hook.DisableAllHooksExcept(event, stringID)
|
|
assert(type(event) == 'string', 'hook.DisableAllHooksExcept - event is not a string! ' .. type(event))
|
|
assert(type(stringID) ~= 'nil', 'hook.DisableAllHooksExcept - ID is not a valid value! ' .. type(stringID))
|
|
|
|
if not __table[event] then return false end
|
|
stringID = transformStringID(stringID, event)
|
|
|
|
for priority = -10, 10 do
|
|
if __table[event][priority] then
|
|
for id, hookData in pairs(__table[event][priority]) do
|
|
if id ~= stringID then
|
|
hookData.disabled = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
hook.Reconstruct(event)
|
|
return true
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.DisableHooksByPredicate
|
|
@args string event, function predicate
|
|
|
|
@desc
|
|
Predicate should return true to disable hook
|
|
and return false to enable hook
|
|
Arguments passed to predicate are `event, id, priority, dlibHookData`
|
|
@enddesc
|
|
|
|
@returns
|
|
boolean
|
|
]]
|
|
function hook.DisableHooksByPredicate(event, predicate)
|
|
assert(type(event) == 'string', 'hook.DisableAllHooksByPredicate - event is not a string! ' .. type(event))
|
|
assert(type(predicate) == 'function', 'hook.DisableAllHooksByPredicate - invalid predicate function! ' .. type(predicate))
|
|
|
|
if not __table[event] then return false end
|
|
|
|
for priority = -10, 10 do
|
|
if __table[event][priority] then
|
|
for id, hookData in pairs(__table[event][priority]) do
|
|
local reply = predicate(event, id, priority, hookData)
|
|
|
|
if reply then
|
|
hookData.disabled = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
hook.Reconstruct(event)
|
|
return true
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.DisableAllHooksByPredicate
|
|
@args function predicate
|
|
|
|
@desc
|
|
same as `hook.DisableHooksByPredicate`, except it is ran for all events
|
|
pass `function() return true end` as predicate to disable **EVERYTHING**
|
|
@enddesc
|
|
|
|
@returns
|
|
boolean
|
|
]]
|
|
function hook.DisableAllHooksByPredicate(predicate)
|
|
assert(type(predicate) == 'function', 'hook.DisableAllHooksByPredicate - invalid predicate function! ' .. type(predicate))
|
|
|
|
for event, eventData in pairs(__table) do
|
|
for priority = -10, 10 do
|
|
if eventData[priority] then
|
|
for id, hookData in pairs(eventData[priority]) do
|
|
local reply = predicate(event, id, priority, hookData)
|
|
|
|
if reply then
|
|
hookData.disabled = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
hook.Reconstruct(event)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.EnableHooksByPredicate
|
|
@args string event, function predicate
|
|
|
|
@desc
|
|
counterpart of `hook.DisableHooksByPredicate`
|
|
@enddesc
|
|
|
|
@returns
|
|
boolean
|
|
]]
|
|
function hook.EnableHooksByPredicate(event, predicate)
|
|
assert(type(event) == 'string', 'hook.DisableAllHooksByPredicate - event is not a string! ' .. type(event))
|
|
assert(type(predicate) == 'function', 'hook.DisableAllHooksByPredicate - invalid predicate function! ' .. type(predicate))
|
|
|
|
if not __table[event] then return false end
|
|
|
|
for priority = -10, 10 do
|
|
if __table[event][priority] then
|
|
for id, hookData in pairs(__table[event][priority]) do
|
|
local reply = predicate(event, id, priority, hookData)
|
|
|
|
if reply then
|
|
hookData.disabled = false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
hook.Reconstruct(event)
|
|
return true
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.EnableAllHooksByPredicate
|
|
@args function predicate
|
|
|
|
@desc
|
|
counterpart of `hook.DisableAllHooksByPredicate`
|
|
@enddesc
|
|
|
|
@returns
|
|
boolean
|
|
]]
|
|
function hook.EnableAllHooksByPredicate(predicate)
|
|
assert(type(predicate) == 'function', 'hook.DisableAllHooksByPredicate - invalid predicate function! ' .. type(predicate))
|
|
|
|
for event, eventData in pairs(__table) do
|
|
for priority = -10, 10 do
|
|
if eventData[priority] then
|
|
for id, hookData in pairs(eventData[priority]) do
|
|
local reply = predicate(event, id, priority, hookData)
|
|
|
|
if reply then
|
|
hookData.disabled = false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
hook.Reconstruct(event)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.EnableAllHooks
|
|
|
|
@returns
|
|
table: enabled hooks (copy of __disabled)
|
|
]]
|
|
function hook.EnableAllHooks()
|
|
local toenable = {}
|
|
|
|
for k, v in pairs(__disabled) do
|
|
table.insert(toenable, k)
|
|
end
|
|
|
|
for i, v in ipairs(toenable) do
|
|
__disabled[v] = nil
|
|
end
|
|
|
|
for event, eventData in pairs(__table) do
|
|
for priority = -10, 10 do
|
|
if eventData[priority] then
|
|
for id, hookData in pairs(eventData[priority]) do
|
|
hookData.disabled = false
|
|
end
|
|
end
|
|
end
|
|
|
|
hook.Reconstruct(event)
|
|
end
|
|
|
|
return toenable
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.EnableHook
|
|
@args string event, any hookID
|
|
|
|
@returns
|
|
boolean
|
|
]]
|
|
function hook.EnableHook(event, stringID)
|
|
assert(type(event) == 'string', 'hook.EnableHook - event is not a string! ' .. type(event))
|
|
|
|
if not stringID then
|
|
if not __disabled[event] then
|
|
return false
|
|
end
|
|
|
|
__disabled[event] = nil
|
|
return true
|
|
end
|
|
|
|
if not __table[event] then return false end
|
|
stringID = transformStringID(stringID, event)
|
|
|
|
for priority = -10, 10 do
|
|
if __table[event][priority] and __table[event][priority][stringID] then
|
|
local wasDisabled = __table[event][priority][stringID].disabled
|
|
__table[event][priority][stringID].disabled = false
|
|
hook.Reconstruct(event)
|
|
return wasDisabled
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.Add
|
|
@replaces
|
|
@args string event, any hookID, function callback, number priority = 0
|
|
|
|
@desc
|
|
Refer to !g:hook.Add for main information
|
|
`priority` can be a number within range of -10 to 10 inclusive
|
|
`hookID` **can** be a number, unlike default gmod behavior, but can not be a boolean
|
|
prints tracebacks when some of arguments are invalid instead of silent fail, unlike original hook
|
|
throws an error when something goes horribly wrong instead of silent fail, unlike original hook
|
|
if priority argument is omitted, then it uses `0` as priority (if hook was not defined before)
|
|
and use previous priority if hook already exists (assuming we want to overwrite old hook definition) unlike ULib's hook
|
|
this can be useful with software which can provide hook benchmarking by re-defining every single hook using hook.Add
|
|
and it doesn't know about hook priorities
|
|
@enddesc
|
|
]]
|
|
function hook.Add(event, stringID, funcToCall, priority)
|
|
if type(event) ~= 'string' then
|
|
--if DLib.DEBUG_MODE:GetBool() then
|
|
DLib.Message(traceback('hook.Add - event is not a string! ' .. type(event)))
|
|
--end
|
|
|
|
return
|
|
end
|
|
|
|
__table[event] = __table[event] or {}
|
|
|
|
if type(funcToCall) ~= 'function' then
|
|
--if DLib.DEBUG_MODE:GetBool() then
|
|
DLib.Message(traceback('hook.Add - function is not a function! ' .. type(funcToCall)))
|
|
--end
|
|
|
|
return
|
|
end
|
|
|
|
stringID = transformStringID(stringID, event)
|
|
|
|
for priority = maximalPriority, minimalPriority do
|
|
local eventsTable = __table[event][priority]
|
|
|
|
if eventsTable and eventsTable[stringID] then
|
|
if not priority then
|
|
priority = eventsTable[stringID].priority
|
|
end
|
|
|
|
eventsTable[stringID] = nil
|
|
end
|
|
end
|
|
|
|
priority = priority or 0
|
|
|
|
if type(priority) ~= 'number' then
|
|
priority = tonumber(priority) or 0
|
|
end
|
|
|
|
priority = math.Clamp(math.floor(priority), maximalPriority, minimalPriority)
|
|
|
|
local hookData = {
|
|
event = event,
|
|
priority = priority,
|
|
funcToCall = funcToCall,
|
|
fn = funcToCall, -- ULib
|
|
isstring = type(stringID) == 'string', -- ULib
|
|
id = stringID,
|
|
idString = tostring(stringID),
|
|
registeredAt = SysTime(),
|
|
typeof = type(stringID) == 'string'
|
|
}
|
|
|
|
__table[event][priority] = __table[event][priority] or {}
|
|
__table[event][priority][stringID] = hookData
|
|
__tableGmod[event] = __tableGmod[event] or {}
|
|
__tableGmod[event][stringID] = funcToCall
|
|
|
|
hook.Reconstruct(event)
|
|
return
|
|
end
|
|
|
|
hook._O_SALT = hook._O_SALT or -0xFFFFFFF
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.Once
|
|
@replaces
|
|
@args string event, function callback, number priority = 0
|
|
|
|
@desc
|
|
`hook.Add`, but function would be called back only once.
|
|
@enddesc
|
|
]]
|
|
function hook.Once(event, funcToCall, priority)
|
|
hook._O_SALT = hook._O_SALT + 1
|
|
local id = 'hook.Once.' .. hook._O_SALT
|
|
|
|
hook.Add(event, id, function(...)
|
|
hook.Remove(event, id)
|
|
return funcToCall(...)
|
|
end, priority)
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.Remove
|
|
@replaces
|
|
@args string event, any hookID
|
|
]]
|
|
function hook.Remove(event, stringID)
|
|
if type(event) ~= 'string' then
|
|
--if DLib.DEBUG_MODE:GetBool() then
|
|
DLib.Message(traceback('hook.Remove - event is not a string! ' .. type(event)))
|
|
--end
|
|
|
|
return
|
|
end
|
|
|
|
if type(stringID) == 'nil' then
|
|
--if DLib.DEBUG_MODE:GetBool() then
|
|
DLib.Message(traceback('hook.Remove - hook id is nil!'))
|
|
--end
|
|
|
|
return
|
|
end
|
|
|
|
if not __table[event] then return end
|
|
__tableGmod[event] = __tableGmod[event] or {}
|
|
|
|
stringID = transformStringID(stringID, event)
|
|
|
|
__tableGmod[event][stringID] = nil
|
|
|
|
for priority = maximalPriority, minimalPriority do
|
|
local eventsTable = __table[event][priority]
|
|
|
|
if eventsTable ~= nil then
|
|
local oldData = eventsTable[stringID]
|
|
if oldData ~= nil then
|
|
eventsTable[stringID] = nil
|
|
hook.Reconstruct(event)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.AddPostModifier
|
|
@args string event, any hookID, function callback
|
|
|
|
@desc
|
|
Unique feature of DLib hook
|
|
This allows you to define "post-hooks"
|
|
hooks which transform data returned by previous hook
|
|
Hooks bound to event will receive values returned by upvalue hook.
|
|
**This is meant for advanced users only. Use with care and don't try anything stupid!**
|
|
If you somehow don't want to mess with passed arguments, **you must return them back**
|
|
otherwise engine/hook.Run caller/other post modifiers will receive empty values, like none of hooks returned values
|
|
this can be useful for doing "final fixes" to data
|
|
like custom final logic for PlayerSay (local chat/roleyplay for example) or fixes to CalcView
|
|
using this will not affect admin/fun mods (they will use hook (e.g. PlayerSay) as usual)
|
|
and final fixes will always work too despite type of hook
|
|
*Limitation of this hook is that it can not see original arguments passed to* `hook.Run`
|
|
@enddesc
|
|
|
|
@returns
|
|
boolean
|
|
table: hookData
|
|
]]
|
|
function hook.AddPostModifier(event, stringID, funcToCall)
|
|
__tableModifiersPost[event] = __tableModifiersPost[event] or {}
|
|
|
|
if type(event) ~= 'string' then
|
|
--if DLib.DEBUG_MODE:GetBool() then
|
|
DLib.Message(traceback('hook.AddPostModifier - event is not a string! ' .. type(event)))
|
|
--end
|
|
|
|
return false
|
|
end
|
|
|
|
if type(funcToCall) ~= 'function' then
|
|
--if DLib.DEBUG_MODE:GetBool() then
|
|
DLib.Message(traceback('hook.AddPostModifier - function is not a function! ' .. type(funcToCall)))
|
|
--end
|
|
|
|
return false
|
|
end
|
|
|
|
stringID = transformStringID(stringID, event)
|
|
|
|
local hookData = {
|
|
event = event,
|
|
funcToCall = funcToCall,
|
|
id = stringID,
|
|
idString = tostring(stringID),
|
|
registeredAt = SysTime(),
|
|
typeof = type(stringID) == 'string'
|
|
}
|
|
|
|
__tableModifiersPost[event][stringID] = hookData
|
|
hook.ReconstructPostModifiers(event)
|
|
return true, hookData
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.RemovePostModifier
|
|
@args string event, any hookID
|
|
|
|
@returns
|
|
boolean
|
|
table: hookData
|
|
]]
|
|
function hook.RemovePostModifier(event, stringID)
|
|
if not __tableModifiersPost[event] then return false end
|
|
|
|
stringID = transformStringID(stringID, event)
|
|
if __tableModifiersPost[event][stringID] then
|
|
local old = __tableModifiersPost[event][stringID]
|
|
__tableModifiersPost[event][stringID] = nil
|
|
hook.ReconstructPostModifiers(event)
|
|
return true, old
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.ReconstructPostModifiers
|
|
@args string event
|
|
|
|
@internal
|
|
|
|
@desc
|
|
builds optimized hook table
|
|
@enddesc
|
|
|
|
@returns
|
|
table: sorted array of functions
|
|
table: sorted array of hookData
|
|
]]
|
|
function hook.ReconstructPostModifiers(eventToReconstruct)
|
|
if not eventToReconstruct then
|
|
for event, tab in pairs(__tableModifiersPost) do
|
|
hook.ReconstructPostModifiers(event)
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
__tableModifiersPostOptimized[eventToReconstruct] = {}
|
|
local event = __tableModifiersPost[eventToReconstruct]
|
|
|
|
local ordered = {}
|
|
|
|
if event then
|
|
for stringID, hookData in pairs(event) do
|
|
if hookData.typeof == false then
|
|
if hookData.id:IsValid() then
|
|
table.insert(ordered, hookData)
|
|
else
|
|
event[stringID] = nil
|
|
end
|
|
else
|
|
table.insert(ordered, hookData)
|
|
end
|
|
end
|
|
end
|
|
|
|
local cnt = #ordered
|
|
|
|
if cnt == 0 then
|
|
__tableModifiersPostOptimized[eventToReconstruct] = nil
|
|
else
|
|
local target = __tableModifiersPostOptimized[eventToReconstruct]
|
|
|
|
for i = 1, cnt do
|
|
table.insert(target, ordered[i].funcToCall)
|
|
end
|
|
end
|
|
|
|
return __tableModifiersPostOptimized, ordered
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.ListAllHooks
|
|
@args boolean includeDisabled = true
|
|
|
|
@returns
|
|
table: sorted array of hookData
|
|
]]
|
|
function hook.ListAllHooks(includeDisabled)
|
|
if includeDisabled == nil then includeDisabled = true end
|
|
local output = {}
|
|
|
|
for event, priorityTable in pairs(__table) do
|
|
for priority = maximalPriority, minimalPriority do
|
|
local hookList = priorityTable[priority]
|
|
|
|
if hookList then
|
|
for stringID, hookData in pairs(hookList) do
|
|
if not hookData.disabled or includeDisabled then
|
|
table.insert(output, hookData)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return output
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.Reconstruct
|
|
@args string event
|
|
|
|
@internal
|
|
|
|
@desc
|
|
builds optimized hook table
|
|
@enddesc
|
|
|
|
@returns
|
|
table: sorted array of functions
|
|
table: sorted array of hookData
|
|
]]
|
|
function hook.Reconstruct(eventToReconstruct)
|
|
if not eventToReconstruct then
|
|
for event, data in pairs(__table) do
|
|
hook.Reconstruct(event)
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
__tableOptimized[eventToReconstruct] = {}
|
|
local ordered = {}
|
|
local priorityTable = __table[eventToReconstruct]
|
|
local inboundgmod = __tableGmod[eventToReconstruct]
|
|
|
|
if priorityTable then
|
|
for priority = maximalPriority, minimalPriority do
|
|
local hookList = priorityTable[priority]
|
|
|
|
if hookList then
|
|
for stringID, hookData in pairs(hookList) do
|
|
if not hookData.disabled then
|
|
if hookData.typeof == false then
|
|
if hookData.id:IsValid() then
|
|
table.insert(ordered, hookData)
|
|
else
|
|
hookList[stringID] = nil
|
|
inboundgmod[stringID] = nil
|
|
end
|
|
else
|
|
table.insert(ordered, hookData)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local cnt = #ordered
|
|
|
|
if cnt == 0 then
|
|
__tableOptimized[eventToReconstruct] = nil
|
|
else
|
|
local target = __tableOptimized[eventToReconstruct]
|
|
|
|
for i = 1, cnt do
|
|
local callable
|
|
local hookData = ordered[i]
|
|
|
|
if type(hookData.id) == 'string' then
|
|
callable = hookData.funcToCall
|
|
else
|
|
local self = hookData.id
|
|
local upfuncCallableSelf = hookData.funcToCall
|
|
|
|
callable = function(...)
|
|
if not self:IsValid() then
|
|
hook.Remove(hookData.event, self)
|
|
return
|
|
end
|
|
|
|
return upfuncCallableSelf(self, ...)
|
|
end
|
|
end
|
|
|
|
if hook.PROFILING then
|
|
local THIS_RUNTIME = 0
|
|
local THIS_CALLS = 0
|
|
local upfuncProfiled = callable
|
|
|
|
callable = function(...)
|
|
THIS_CALLS = THIS_CALLS + 1
|
|
local t = SysTime()
|
|
local Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M = upfuncProfiled(...)
|
|
local t2 = SysTime()
|
|
|
|
THIS_RUNTIME = THIS_RUNTIME + (t2 - t)
|
|
return Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M
|
|
end
|
|
|
|
hookData.profileEnds = function()
|
|
hookData.THIS_RUNTIME = THIS_RUNTIME
|
|
hookData.THIS_CALLS = THIS_CALLS
|
|
end
|
|
end
|
|
|
|
table.insert(target, callable)
|
|
end
|
|
end
|
|
|
|
return __tableOptimized, ordered
|
|
end
|
|
|
|
local function Call(...)
|
|
return hook.Call2(...)
|
|
end
|
|
|
|
local function Run(...)
|
|
return hook.Run2(...)
|
|
end
|
|
|
|
local __breakage1 = {
|
|
'HUDPaint',
|
|
'PreDrawHUD',
|
|
'PostDrawHUD',
|
|
'Initialize',
|
|
'InitPostEntity',
|
|
'PreGamemodeInit',
|
|
'PostGamemodeInit',
|
|
'PostGamemodeInitialize',
|
|
'PreGamemodeInitialize',
|
|
'PostGamemodeLoaded',
|
|
'PreGamemodeLoaded',
|
|
'PostRenderVGUI',
|
|
'OnGamemodeLoaded',
|
|
|
|
'CreateMove',
|
|
'StartCommand',
|
|
'SetupMove',
|
|
}
|
|
|
|
local __breakage = {}
|
|
|
|
for i, str in ipairs(__breakage1) do
|
|
__breakage[str] = true
|
|
end
|
|
|
|
-- these hooks can't return any values
|
|
hook.StaticHooks = __breakage
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.HasHooks
|
|
@args string event
|
|
|
|
@returns
|
|
boolean
|
|
]]
|
|
function hook.HasHooks(event)
|
|
return __tableOptimized[event] ~= nil and #__tableOptimized[event] ~= 0
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.CallStatic
|
|
@args string event, table hookTable, vararg arguments
|
|
|
|
@desc
|
|
functions called can not interrupt call loop by returning arguments
|
|
can not return arguments
|
|
internall used to call some of most popular hooks
|
|
which will break the game if at least one function in hook list will return value
|
|
@enddesc
|
|
|
|
@internal
|
|
]]
|
|
function hook.CallStatic(event, hookTable, ...)
|
|
local post = __tableModifiersPostOptimized[event]
|
|
local events = __tableOptimized[event]
|
|
|
|
if events == nil then
|
|
if hookTable == nil then
|
|
return
|
|
end
|
|
|
|
local gamemodeFunction = hookTable[event]
|
|
|
|
if gamemodeFunction == nil then
|
|
return
|
|
end
|
|
|
|
return gamemodeFunction(hookTable, ...)
|
|
end
|
|
|
|
local i = 1
|
|
local nextevent = events[i]
|
|
|
|
::loop::
|
|
nextevent(...)
|
|
i = i + 1
|
|
nextevent = events[i]
|
|
|
|
if nextevent ~= nil then
|
|
goto loop
|
|
end
|
|
|
|
if hookTable == nil then
|
|
return
|
|
end
|
|
|
|
local gamemodeFunction = hookTable[event]
|
|
|
|
if gamemodeFunction == nil then
|
|
return
|
|
end
|
|
|
|
return gamemodeFunction(hookTable, ...)
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.Call
|
|
@replaces
|
|
@args string event, table hookTable, vararg arguments
|
|
|
|
@returns
|
|
vararg: values
|
|
]]
|
|
function hook.Call2(event, hookTable, ...)
|
|
if __disabled[event] then
|
|
return
|
|
end
|
|
|
|
ITERATING = event
|
|
|
|
if __breakage[event] == true then
|
|
hook.CallStatic(event, hookTable, ...)
|
|
return
|
|
end
|
|
|
|
local post = __tableModifiersPostOptimized[event]
|
|
local events = __tableOptimized[event]
|
|
|
|
if events == nil then
|
|
if hookTable == nil then
|
|
return
|
|
end
|
|
|
|
local gamemodeFunction = hookTable[event]
|
|
|
|
if gamemodeFunction == nil then
|
|
return
|
|
end
|
|
|
|
if post == nil then
|
|
return gamemodeFunction(hookTable, ...)
|
|
end
|
|
|
|
local Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M = gamemodeFunction(hookTable, ...)
|
|
local i = 1
|
|
local nextevent = post[i]
|
|
|
|
::post_mloop1::
|
|
Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M = nextevent(Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M)
|
|
|
|
i = i + 1
|
|
nextevent = post[i]
|
|
|
|
if nextevent ~= nil then
|
|
goto post_mloop1
|
|
end
|
|
|
|
return Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M
|
|
end
|
|
|
|
local i = 1
|
|
local nextevent = events[i]
|
|
|
|
::loop::
|
|
local Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M = nextevent(...)
|
|
|
|
if Q ~= nil then
|
|
if post == nil then
|
|
return Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M
|
|
end
|
|
|
|
local i = 1
|
|
local nextevent = post[i]
|
|
|
|
::post_mloop2::
|
|
Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M = nextevent(Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M)
|
|
|
|
i = i + 1
|
|
nextevent = post[i]
|
|
|
|
if nextevent ~= nil then
|
|
goto post_mloop2
|
|
end
|
|
|
|
return Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M
|
|
end
|
|
|
|
i = i + 1
|
|
nextevent = events[i]
|
|
|
|
if nextevent ~= nil then
|
|
goto loop
|
|
end
|
|
|
|
if hookTable == nil then
|
|
return
|
|
end
|
|
|
|
local gamemodeFunction = hookTable[event]
|
|
|
|
if gamemodeFunction == nil then
|
|
return
|
|
end
|
|
|
|
if post == nil then
|
|
return gamemodeFunction(hookTable, ...)
|
|
end
|
|
|
|
local Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M = gamemodeFunction(hookTable, ...)
|
|
local i = 1
|
|
local nextevent = post[i]
|
|
|
|
::post_mloop3::
|
|
Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M = nextevent(Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M)
|
|
|
|
i = i + 1
|
|
nextevent = post[i]
|
|
|
|
if nextevent ~= nil then
|
|
goto post_mloop3
|
|
end
|
|
|
|
return Q, W, E, R, T, Y, U, I, O, P, A, S, D, F, G, H, J, K, L, Z, X, C, V, B, N, M
|
|
end
|
|
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.Run
|
|
@replaces
|
|
@args string event, vararg arguments
|
|
|
|
@returns
|
|
vararg: values
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname gamemode.Call
|
|
@replaces
|
|
@args string event, vararg arguments
|
|
|
|
@returns
|
|
vararg: values
|
|
]]
|
|
if gmod then
|
|
local GetGamemode = gmod.GetGamemode
|
|
|
|
function hook.Run2(event, ...)
|
|
return hook.Call2(event, GetGamemode(), ...)
|
|
end
|
|
|
|
function gamemode.Call(event, ...)
|
|
local gm = GetGamemode()
|
|
|
|
if gm == nil then return false end
|
|
if gm[event] == nil then return false end
|
|
|
|
return hook.Call2(event, gm, ...)
|
|
end
|
|
else
|
|
function hook.Run2(event, ...)
|
|
return hook.Call2(event, GAMEMODE, ...)
|
|
end
|
|
|
|
function gamemode.Call()
|
|
return false
|
|
end
|
|
end
|
|
|
|
for k, v in pairs(hook) do
|
|
if type(v) == 'function' then
|
|
hook[k:sub(1, 1):lower() .. k:sub(2)] = v
|
|
end
|
|
end
|
|
|
|
-- Engine permanently remembers function address
|
|
-- So we need to transmit the call to our subfunction in order to modify it on the fly (with no runtime costs because JIT is <3)
|
|
-- and local "hook" will never point at wrong table
|
|
|
|
hook.Call = Call
|
|
hook.Run = Run
|
|
hook.GetTable = GetTable
|
|
|
|
if oldHooks then
|
|
for event, priorityTable in pairs(oldHooks) do
|
|
for priority, hookTable in pairs(priorityTable) do
|
|
for hookID, hookFunc in pairs(hookTable) do
|
|
hook.Add(event, hookID, hookFunc.fn, priority)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
setmetatable(hook, {
|
|
__call = function(self, ...)
|
|
return self.Add(...)
|
|
end
|
|
})
|
|
|
|
if ghook ~= DLib.ghook and ents.GetCount() < 10 then
|
|
DLib.ghook = ghook
|
|
|
|
for k, v in pairs(ghook) do
|
|
rawset(ghook, k, nil)
|
|
end
|
|
|
|
setmetatable(DLib.ghook, {
|
|
__index = hook,
|
|
|
|
__newindex = function(self, key, value)
|
|
if hook[key] == value then return end
|
|
|
|
if DLib.DEBUG_MODE:GetBool() then
|
|
DLib.Message(traceback('DEPRECATED: Do NOT mess with hook system directly! https://goo.gl/NDAQqY\nReport this message to addon author which is involved in this stack trace:\nhook.' .. tostring(key) .. ' (' .. tostring(hook[key]) .. ') -> ' .. tostring(value), 2))
|
|
end
|
|
|
|
local status = hook.Call('DLibHookChange', nil, key, value)
|
|
if status == false then return end
|
|
rawset(hook, key, value)
|
|
end,
|
|
|
|
__call = function(self, ...)
|
|
return self.Add(...)
|
|
end
|
|
})
|
|
elseif ghook ~= DLib.ghook then
|
|
function ghook.AddPostModifier()
|
|
|
|
end
|
|
end
|
|
|
|
DLib.benchhook = {
|
|
Add = hook.Add,
|
|
Call = hook.Call2,
|
|
Run = hook.Run2,
|
|
Remove = hook.Remove,
|
|
GetTable = hook.GetTable,
|
|
}
|
|
|
|
local function lua_findhooks(eventName, ply)
|
|
DLib.MessagePlayer(ply, '----------------------------------')
|
|
DLib.MessagePlayer(ply, string.format('Finding %s hooks for event %q', CLIENT and 'CLIENTSIDE' or 'SERVERSIDE', eventName))
|
|
|
|
local tableToUse = __table[eventName]
|
|
|
|
if tableToUse and table.Count(tableToUse) ~= 0 then
|
|
for priority = maximalPriority, minimalPriority do
|
|
local hookList = tableToUse[priority]
|
|
|
|
if hookList then
|
|
for stringID, hookData in pairs(hookList) do
|
|
local info = debug.getinfo(hookData.funcToCall)
|
|
DLib.MessagePlayer(ply,
|
|
string.format(
|
|
'\t\t%q [%s] at %p (%s: %i->%i)',
|
|
stringID,
|
|
priority,
|
|
hookData.funcToCall,
|
|
info.source,
|
|
info.linedefined,
|
|
info.lastlinedefined
|
|
)
|
|
)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
DLib.MessagePlayer(ply, 'No hooks defined for specified event')
|
|
end
|
|
|
|
DLib.MessagePlayer(ply, '----------------------------------')
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname hook.GetDumpStr
|
|
|
|
@internal
|
|
|
|
@returns
|
|
string
|
|
]]
|
|
function hook.GetDumpStr()
|
|
local lines = {}
|
|
|
|
local sorted = {}
|
|
|
|
for eventName, eventData in pairs(__table) do
|
|
table.insert(sorted, eventName)
|
|
end
|
|
|
|
table.sort(sorted)
|
|
|
|
for i, eventName in ipairs(sorted) do
|
|
local eventData = __table[eventName]
|
|
|
|
for priority = maximalPriority, minimalPriority do
|
|
local hookList = eventData[priority]
|
|
|
|
if hookList then
|
|
local llines = {}
|
|
table.insert(lines, '// Begin list hooks of event ' .. eventName)
|
|
|
|
for stringID, hookData in pairs(hookList) do
|
|
local info = debug.getinfo(hookData.funcToCall)
|
|
|
|
table.insert(llines,
|
|
string.format(
|
|
'\t%q [%s] at %p (%s: %i->%i)',
|
|
tostring(stringID),
|
|
tostring(priority),
|
|
hookData.funcToCall,
|
|
info.source,
|
|
info.linedefined,
|
|
info.lastlinedefined
|
|
)
|
|
)
|
|
end
|
|
|
|
table.sort(llines)
|
|
table.append(lines, llines)
|
|
|
|
table.insert(lines, '// End list hooks of event ' .. eventName .. '\n')
|
|
end
|
|
end
|
|
end
|
|
|
|
return table.concat(lines, '\n')
|
|
end
|
|
|
|
local function lua_findhooks_cl(ply, cmd, args)
|
|
if not game.SinglePlayer() and IsValid(ply) and ply:IsPlayer() and not ply:IsAdmin() then return end
|
|
|
|
if not args[1] then
|
|
DLib.Message('No event name were provided!')
|
|
return
|
|
end
|
|
|
|
lua_findhooks(table.concat(args, ' '):trim(), ply)
|
|
end
|
|
|
|
local function lua_findhooks_sv(ply, cmd, args)
|
|
if not game.SinglePlayer() and IsValid(ply) and ply:IsPlayer() and not ply:IsAdmin() then return end
|
|
|
|
if not args[1] then
|
|
DLib.Message('No event name were provided!')
|
|
return
|
|
end
|
|
|
|
lua_findhooks(table.concat(args, ' '):trim(), ply)
|
|
end
|
|
|
|
do
|
|
local function autocomplete(cmd, args)
|
|
args = args:lower():trim()
|
|
|
|
if args[1] == '"' then
|
|
args = args:sub(2)
|
|
end
|
|
|
|
if args[#args] == '"' then
|
|
args = args:sub(1, #args - 1)
|
|
end
|
|
|
|
local output = {}
|
|
|
|
for k, v in pairs(__tableGmod) do
|
|
if k:lower():startsWith(args) then
|
|
table.insert(output, cmd .. ' "' .. k .. '"')
|
|
end
|
|
end
|
|
|
|
table.sort(output)
|
|
|
|
return output
|
|
end
|
|
|
|
timer.Simple(0, function()
|
|
if CLIENT then
|
|
concommand.Add('lua_findhooks_cl', lua_findhooks_cl, autocomplete)
|
|
else
|
|
concommand.Add('lua_findhooks', lua_findhooks_sv, autocomplete)
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function printProfilingResults(ply)
|
|
local deftable = {}
|
|
|
|
local totalRuntime = 0
|
|
|
|
for i, hookData in ipairs(hook.ListAllHooks(false)) do
|
|
deftable[hookData.event] = deftable[hookData.event] or {runtime = 0, calls = 0, list = {}, name = hookData.event}
|
|
|
|
table.insert(deftable[hookData.event].list, hookData)
|
|
deftable[hookData.event].runtime = deftable[hookData.event].runtime + hookData.THIS_RUNTIME
|
|
totalRuntime = totalRuntime + hookData.THIS_RUNTIME
|
|
deftable[hookData.event].calls = deftable[hookData.event].calls + hookData.THIS_CALLS
|
|
end
|
|
|
|
local sortedtable = {}
|
|
|
|
for event, eventTable in pairs(deftable) do
|
|
table.sort(eventTable.list, function(a, b)
|
|
return a.THIS_RUNTIME > b.THIS_RUNTIME
|
|
end)
|
|
|
|
table.insert(sortedtable, eventTable)
|
|
end
|
|
|
|
table.sort(sortedtable, function(a, b)
|
|
return a.runtime > b.runtime
|
|
end)
|
|
|
|
DLib.MessagePlayer(ply, '-----------------------------------')
|
|
DLib.MessagePlayer(ply, '------ HOOK PROFILING REPORT ------')
|
|
DLib.MessagePlayer(ply, '-----------------------------------')
|
|
|
|
local time = hook.PROFILE_ENDS - hook.PROFILE_STARTED
|
|
|
|
for pos, eventTable in ipairs(sortedtable) do
|
|
if pos > 10 then
|
|
DLib.MessagePlayer(ply, '... tail of events ... (', #sortedtable - 10, ' are not shown)')
|
|
break
|
|
end
|
|
|
|
DLib.MessagePlayer(ply, '/// ' .. eventTable.name .. ': Runtime position: ', pos, string.format(' (%.2f%% of game runtime); - Total hook calls: ', (eventTable.runtime / time) * 100), eventTable.calls,
|
|
string.format('; Total runtime: %.2f milliseconds (~%.2f microseconds per hook call on average)', eventTable.runtime * 1000, (eventTable.runtime * 1000000) / eventTable.calls))
|
|
|
|
for pos2, hookData in ipairs(eventTable.list) do
|
|
if hookData.THIS_RUNTIME <= 0.001 then
|
|
DLib.MessagePlayer(ply, '(', #eventTable.list - pos2 + 1, ' are not shown)')
|
|
break
|
|
end
|
|
|
|
DLib.MessagePlayer(ply, 'Hook ID: ', hookData.id)
|
|
DLib.MessagePlayer(ply, string.format('\t - Runtime: %.2f milliseconds; %i calls; ~%.2f microseconds per call on average',
|
|
hookData.THIS_RUNTIME * 1000, hookData.THIS_CALLS, (hookData.THIS_RUNTIME * 1000000) / hookData.THIS_CALLS))
|
|
end
|
|
end
|
|
|
|
DLib.MessagePlayer(ply, '--')
|
|
DLib.MessagePlayer(ply, 'In total, regular hooks took around ', math.floor((totalRuntime / time) * 10000) / 100, '% of game runtime.')
|
|
DLib.MessagePlayer(ply, '--')
|
|
|
|
DLib.MessagePlayer(ply, '-----------------------------------')
|
|
DLib.MessagePlayer(ply, '--- END OF HOOK PROFILING REPORT --')
|
|
DLib.MessagePlayer(ply, '-----------------------------------')
|
|
end
|
|
|
|
if CLIENT then
|
|
concommand.Add('dlib_profile_hooks_cl', function(ply, cmd, args)
|
|
if hook.PROFILING then
|
|
hook.PROFILE_ENDS = SysTime()
|
|
hook.PROFILING = false
|
|
hook.PROFILING_RESULTS_EXISTS = true
|
|
hook.Reconstruct()
|
|
|
|
for i, hookData in ipairs(hook.ListAllHooks(false)) do
|
|
hookData.profileEnds()
|
|
end
|
|
|
|
printProfilingResults(LocalPlayer())
|
|
return
|
|
end
|
|
|
|
hook.PROFILE_STARTED = SysTime()
|
|
hook.PROFILING = true
|
|
|
|
DLib.Message('Hook profiling were started')
|
|
DLib.Message('When you are ready you can type dlib_profile_hooks_cl again')
|
|
DLib.Message('/// NOTE THAT RESULTS BECOME MORE ACCURATE AS PROFILING GOES!')
|
|
DLib.Message('/// Disabling it too early will produce false results')
|
|
hook.Reconstruct()
|
|
end)
|
|
|
|
concommand.Add('dlib_profile_hooks_last_cl', function(ply, cmd, args)
|
|
if not hook.PROFILING_RESULTS_EXISTS then
|
|
DLib.Message('No profiling results exists!')
|
|
DLib.Message('Start a new one by typing dlib_profile_hooks_cl')
|
|
return
|
|
end
|
|
|
|
printProfilingResults(LocalPlayer())
|
|
end)
|
|
else
|
|
concommand.Add('dlib_profile_hooks_last_sv', function(ply, cmd, args)
|
|
if IsValid(ply) and not ply:IsSuperAdmin() then
|
|
DLib.MessagePlayer(ply, 'Not a super admin!')
|
|
return
|
|
end
|
|
|
|
if not hook.PROFILING_RESULTS_EXISTS then
|
|
DLib.MessagePlayer(ply, 'No profiling results exists!')
|
|
DLib.MessagePlayer(ply, 'Start a new one by typing dlib_profile_hooks_cl')
|
|
return
|
|
end
|
|
|
|
DLib.Message(IsValid(ply) and ply or 'Console', ' requested hook profiling results')
|
|
printProfilingResults(ply)
|
|
end)
|
|
|
|
concommand.Add('dlib_profile_hooks_sv', function(ply, cmd, args)
|
|
if IsValid(ply) and not ply:IsSuperAdmin() then
|
|
DLib.MessagePlayer(ply, 'Not a super admin!')
|
|
return
|
|
end
|
|
|
|
if hook.PROFILING then
|
|
DLib.Message(IsValid(ply) and ply or 'Console', ' stopped hook profiling')
|
|
hook.PROFILE_ENDS = SysTime()
|
|
hook.PROFILING = false
|
|
hook.PROFILING_RESULTS_EXISTS = true
|
|
hook.Reconstruct()
|
|
|
|
for i, hookData in ipairs(hook.ListAllHooks(false)) do
|
|
hookData.profileEnds()
|
|
end
|
|
|
|
printProfilingResults(ply)
|
|
return
|
|
end
|
|
|
|
DLib.Message(IsValid(ply) and ply or 'Console', ' started hook profiling')
|
|
|
|
hook.PROFILE_STARTED = SysTime()
|
|
hook.PROFILING = true
|
|
|
|
DLib.MessagePlayer(ply, 'Hook profiling were started')
|
|
DLib.MessagePlayer(ply, 'When you are ready you can type dlib_profile_hooks_cl again')
|
|
DLib.MessagePlayer(ply, '/// NOTE THAT RESULTS BECOME MORE ACCURATE AS PROFILING GOES!')
|
|
DLib.MessagePlayer(ply, '/// Disabling it too early will produce false results')
|
|
hook.Reconstruct()
|
|
end)
|
|
end
|
|
|
|
if file.Exists('autorun/hat_init.lua', 'LUA') then
|
|
DLib.Message(string.rep('-', 63))
|
|
DLib.Message(string.rep('W', 63))
|
|
DLib.Message(string.rep('A', 63))
|
|
DLib.Message(string.rep('R', 63))
|
|
DLib.Message(string.rep('N', 63))
|
|
DLib.Message(string.rep('I', 63))
|
|
DLib.Message(string.rep('N', 63))
|
|
DLib.Message(string.rep('G', 63))
|
|
DLib.Message('HAT INSTALLATION DETECTED')
|
|
DLib.Message('HAT IS BASICALLY BROKEN FOR YEARS')
|
|
DLib.Message('AND IT ALSO BREAK THE GAME, OBVIOUSLY')
|
|
DLib.Message('IMPLEMENTING HAT LOADER TRAP')
|
|
DLib.Message(string.rep('-', 63))
|
|
|
|
table._DLibCopy = table._DLibCopy or table.Copy
|
|
|
|
function table.Copy(tableIn)
|
|
if tableIn == _G.concommand or tableIn == _G.hook then
|
|
table.Copy = table._DLibCopy
|
|
error('Nuh uh. No. Definitely Not. I dont even.', 2)
|
|
end
|
|
|
|
return table._DLibCopy(tableIn)
|
|
end
|
|
end
|