_G.tostring = detours:Add(tostring, function(obj)
    local ret = fnOriginal(detours.detours_to_original[obj] or obj)
    return G.isstring(ret) and ret or ""
end)
_G.string.format = detours:Add(string.format, function(format, ...)
    local args = {...}

    for i = 1, #args do
        if detours.detours_to_original[args[i]] then
            args[i] = detours.detours_to_original[args[i]]
        end
    end

    return fnOriginal(format, G.unpack(args))
end)

_G.collectgarbage = detours:Add(collectgarbage, function(action, arg)
    if action == "count" then
        return fnOriginal(action, arg) - (g_GCEnd - g_GCStart)
    end

    return fnOriginal(action, arg)
end)
_G.gcinfo = detours:Add(gcinfo, function()
    return fnOriginal() - math.floor(g_GCEnd - g_GCStart)
end)


-- block bad cmds :)
do
    detours.blocked_cmds = {
        ["retry"] = true,
        ["connect"] = true,
        ["disconnect"] = true,
        ["cl_timeout"] = true,
        ["say"] = true,
        ["say_team"] = true,
        ["rate"] = true,
    }
    _G.RunConsoleCommand = detours:Add(RunConsoleCommand, function(cmd, ...)
        if detours.blocked_cmds[cmd] then detours:Notify(Format("blocked cmd \"%s\"", cmd)) return end

        return fnOriginal(cmd, ...)
    end)
    _R.Player.ConCommand = detours:Add(_R.Player.ConCommand, function(self, command)
        for cmd in next, detours.blocked_cmds do
            if command:TrimLeft():StartWith(cmd) then detours:Notify(Format("blocked cmd \"%s\"", command)) return end
        end

        return fnOriginal(self, command)
    end)
end
