LUA скрипт Gamesense watermark

Начинающий
Статус
Оффлайн
Регистрация
14 Июл 2022
Сообщения
91
Реакции[?]
5
Поинты[?]
4K
Код:
-- local variables for API functions. any changes to the line below will be lost on re-generation
local client_latency, client_screen_size, client_set_event_callback, client_system_time, entity_get_local_player, entity_get_player_resource, entity_get_prop, globals_absoluteframetime, globals_tickinterval, math_ceil, math_floor, math_min, math_sqrt, renderer_measure_text, ui_reference, pcall, renderer_gradient, renderer_rectangle, renderer_text, string_format, table_insert, ui_get, ui_new_checkbox, ui_new_color_picker, ui_new_multiselect, ui_new_textbox, ui_set, ui_set_callback, ui_set_visible = client.latency, client.screen_size, client.set_event_callback, client.system_time, entity.get_local_player, entity.get_player_resource, entity.get_prop, globals.absoluteframetime, globals.tickinterval, math.ceil, math.floor, math.min, math.sqrt, renderer.measure_text, ui.reference, pcall, renderer.gradient, renderer.rectangle, renderer.text, string.format, table.insert, ui.get, ui.new_checkbox, ui.new_color_picker, ui.new_multiselect, ui.new_textbox, ui.set, ui.set_callback, ui.set_visible

local flag, old_renderer_text, old_renderer_measure_text = "d", renderer_text, renderer_measure_text
function renderer_text(x, y, r, g, b, a, flags, max_width, ...)
    return old_renderer_text(x, y, r, g, b, a, flags == nil and flag or flag .. flags, max_width, ...)
end
function renderer_measure_text(flags, ...)
    return old_renderer_measure_text(flags == nil and flag or flag .. flags, ...)
end

local allow_unsafe_scripts = pcall(client.create_interface)

local FLOW_OUTGOING, FLOW_INCOMING = 0, 1
local native_GetNetChannelInfo, GetRemoteFramerate, native_GetTimeSinceLastReceived, native_GetAvgChoke, native_GetAvgLoss, native_IsLoopback, GetAddress

if allow_unsafe_scripts then
    local ffi = require "ffi"

    local function vmt_entry(instance, index, type)
        return ffi.cast(type, (ffi.cast("void***", instance)[0])[index])
    end

    local function vmt_thunk(index, typestring)
        local t = ffi.typeof(typestring)
        return function(instance, ...)
            assert(instance ~= nil)
            if instance then
                return vmt_entry(instance, index, t)(instance, ...)
            end
        end
    end

    local function vmt_bind(module, interface, index, typestring)
        local instance = client.create_interface(module, interface) or error("invalid interface")
        local fnptr = vmt_entry(instance, index, ffi.typeof(typestring)) or error("invalid vtable")
        return function(...)
            return fnptr(instance, ...)
        end
    end

    native_GetNetChannelInfo = vmt_bind("engine.dll", "VEngineClient014", 78, "void*(__thiscall*)(void*)")
    local native_GetName = vmt_thunk(0, "const char*(__thiscall*)(void*)")
    local native_GetAddress = vmt_thunk(1, "const char*(__thiscall*)(void*)")
    native_IsLoopback = vmt_thunk(6, "bool(__thiscall*)(void*)")
    local native_IsTimingOut = vmt_thunk(7, "bool(__thiscall*)(void*)")
    native_GetAvgLoss = vmt_thunk(11, "float(__thiscall*)(void*, int)")
    native_GetAvgChoke = vmt_thunk(12, "float(__thiscall*)(void*, int)")
    native_GetTimeSinceLastReceived = vmt_thunk(22, "float(__thiscall*)(void*)")
    local native_GetRemoteFramerate = vmt_thunk(25, "void(__thiscall*)(void*, float*, float*, float*)")
    local native_GetTimeoutSeconds = vmt_thunk(26, "float(__thiscall*)(void*)")

    local pflFrameTime = ffi.new("float[1]")
    local pflFrameTimeStdDeviation = ffi.new("float[1]")
    local pflFrameStartTimeStdDeviation = ffi.new("float[1]")

    function GetRemoteFramerate(netchannelinfo)
        native_GetRemoteFramerate(netchannelinfo, pflFrameTime, pflFrameTimeStdDeviation, pflFrameStartTimeStdDeviation)
        if pflFrameTime ~= nil and pflFrameTimeStdDeviation ~= nil and pflFrameStartTimeStdDeviation ~= nil then
            return pflFrameTime[0], pflFrameTimeStdDeviation[0], pflFrameStartTimeStdDeviation[0]
        end
    end

    function GetAddress(netchannelinfo)
        local addr = native_GetAddress(netchannelinfo)
        if addr ~= nil then
            return ffi.string(addr)
        end
    end

    local function GetName(netchannelinfo)
        local name = native_GetName(netchannelinfo)
        if name ~= nil then
            return ffi.string(name)
        end
    end
end

local cvar_game_mode, cvar_game_type, cvar_fps_max, cvar_fps_max_menu = cvar.game_mode, cvar.game_type, cvar.fps_max, cvar.fps_max_menu
local table_clear = require "table.clear"

-- initialize window
local window = ((function() local a={}local b,c,d,e,f=renderer.rectangle,renderer.gradient,renderer.texture,math.floor,math.ceil;local function g(h,i,j,k,l,m,n,o,p)p=p or 1;b(h,i,j,p,l,m,n,o)b(h,i+k-p,j,p,l,m,n,o)b(h,i+p,p,k-p*2,l,m,n,o)b(h+j-p,i+p,p,k-p*2,l,m,n,o)end;local function q(h,i,j,k,r,s,t,u,v,w,x,y,z,p)p=p or 1;if z then b(h,i,p,k,r,s,t,u)b(h+j-p,i,p,k,v,w,x,y)c(h+p,i,j-p*2,p,r,s,t,u,v,w,x,u,true)c(h+p,i+k-p,j-p*2,p,r,s,t,u,v,w,x,u,true)else b(h,i,j,p,r,s,t,u)b(h,i+k-p,j,p,v,w,x,y)c(h,i+p,p,k-p*2,r,s,t,u,v,w,x,y,false)c(h+j-p,i+p,p,k-p*2,r,s,t,u,v,w,x,y,false)end end;local A;do local B="\x14\x14\x14\xFF"local C="\x0c\x0c\x0c\xFF"A=renderer.load_rgba(table.concat({B,B,B,C,B,C,B,C,B,C,B,B,B,C,B,C}),4,4)end;local function D(E,F)if F~=nil and type(E)=="string"and E:sub(-1,-1)=="%"then E=math.floor(tonumber(E:sub(1,-2))/100*F)end;return E end;local function G(H)if H.position=="fixed"then local I,J=client.screen_size()if H.left~=nil then H.x=D(H.left,I)elseif H.right~=nil then H.x=I-(H.w or 0)-D(H.right,I)end;if H.top~=nil then H.y=D(H.top,J)elseif H.bottom~=nil then H.y=J-(H.h or 0)-D(H.bottom,J)end end;local h,i,j,k,o=H.x,H.y,H.w,H.h,H.a or 255;local K=1;if h==nil or i==nil or j==nil or o==nil then return end;H.i_x,H.i_y,H.i_w,H.i_h=H.x,H.y,H.w,H.h;if H.title_bar then K=(H.title~=nil and select(2,renderer.measure_text(H.title_text_size,H.title))or 13)+2;H.t_x,H.t_y,H.t_w,H.t_h=H.x,H.y,H.w,K end;if H.border then g(h,i,j,k,18,18,18,o)g(h+1,i+1,j-2,k-2,62,62,62,o)g(h+2,i+K+1,j-4,k-K-3,44,44,44,o,H.border_width)g(h+H.border_width+2,i+K+H.border_width+1,j-H.border_width*2-4,k-K-H.border_width*2-3,62,62,62,o)H.i_x=H.i_x+H.border_width+3;H.i_y=H.i_y+H.border_width+3;H.i_w=H.i_w-(H.border_width+3)*2;H.i_h=H.i_h-(H.border_width+3)*2;H.t_x,H.t_y,H.t_w=H.x+2,H.y+2,H.w-4;K=K-1 end;if K>1 then c(H.t_x,H.t_y,H.t_w,K,56,56,56,o,44,44,44,o,false)if H.title~=nil then local l,m,n,o=unpack(H.title_text_color)o=o*H.a/255;renderer.text(H.t_x+3,H.t_y+2,l or 255,m or 255,n or 255,o or 255,(H.title_text_size or"")..(H.title_text_flags or""),0,tostring(H.title))end;H.i_y=H.i_y+K;H.i_h=H.i_h-K end;if H.gradient_bar then local L=0;if H.background then L=1;local M,N=16,25;b(H.i_x+1,H.i_y,H.i_w-2,1,M,M,M,o)b(H.i_x+1,H.i_y+3,H.i_w-2,1,N,N,N,o)for O=0,1 do c(H.i_x+(H.i_w-1)*O,H.i_y,1,4,M,M,M,o,N,N,N,o,false)end end;do local h,i,P=H.i_x+L,H.i_y+L,1;local Q,R=e((H.i_w-L*2)/2),f((H.i_w-L*2)/2)for O=1,2 do c(h,i,Q,1,59*P,175*P,222*P,o,202*P,70*P,205*P,o,true)c(h+Q,i,R,1,202*P,70*P,205*P,o,201*P,227*P,58*P,o,true)i,P=i+1,P*0.5 end end;H.i_y=H.i_y+2+L*2;H.i_h=H.i_h-2-L*2 end;if H.background then d(A,H.i_x,H.i_y,H.i_w,H.i_h,255,255,255,255,"t")end;if H.draggable then local p=7;renderer.triangle(h+j-1,i+k-p,h+j-1,i+k-1,h+j-p,i+k-1,62,62,62,o)end;H.i_x=H.i_x+H.margin_left;H.i_w=H.i_w-H.margin_left-H.margin_right;H.i_y=H.i_y+H.margin_top;H.i_h=H.i_h-H.margin_top-H.margin_bottom end;local S={}local T={}local U={}local V={__index=U}function U:set_active(W)if W then S[self.id]=self;table.insert(T,1,self.id)else S[self.id]=nil end end;function U:set_z_index(X)self.z_index=X;self.z_index_reset=true end;function U:is_in_window(h,i)return h>=self.x and h<=self.x+self.w and i>=self.y and i<=self.y+self.h end;function U:set_inner_width(Y)if self.border then Y=Y+(self.border_width+3)*2 end;Y=Y+self.margin_left+self.margin_right;self.w=Y end;function U:set_inner_height(Z)local K=1;if self.title_bar then K=(self.title~=nil and select(2,renderer.measure_text(self.title_text_size,self.title))or 13)+2 end;if self.border then Z=Z+(self.border_width+3)*2;K=K-1 end;if K>1 then Z=Z+K end;if self.gradient_bar then local L=0;if self.background then L=1 end;Z=Z+2+L*2 end;Z=Z+self.margin_top+self.margin_bottom;self.h=Z end;function a.new(_,h,i,j,k,a0)local H=setmetatable({id=_,top=h,left=i,w=j,h=k,a=255,paint_callback=a0,title_bar=true,title_bar_in_menu=false,title_text_color={255,255,255,255},title_text_size=nil,gradient_bar=true,border=true,border_width=3,background=true,first=true,visible=true,margin_top=0,margin_bottom=0,margin_left=0,margin_right=0,position="fixed",draggable=false,draggable_save=false,in_menu=false},V)H:set_active(true)return H end;local a1,a2,a3;local function a4(a5)local a6={"bottom","unset","top"}local a7={}for O=#T,1,-1 do local H=S[T[O]]if H~=nil then local a8=H.z_index or"unset"if H.z_index_reset then H.z_index=nil;H.z_index_reset=nil end;a7[a8]=a7[a8]or{}if H.first then table.insert(a7[a8],1,H.id)H.first=nil else table.insert(a7[a8],H.id)end end end;T={}for O=1,#a6 do local a9=a7[a6[O]]if a9~=nil then for O=#a9,1,-1 do table.insert(T,a9[O])end end end;local aa=ui.is_menu_open()local ab={}for O=1,#T do local H=S[T[O]]if H~=nil and H.in_menu==a5 then if H.title_bar_in_menu then H.title_bar=aa end;if H.pre_paint_callback~=nil then H:pre_paint_callback()end;if S[H.id]~=nil then table.insert(ab,S[H.id])end end end;if aa then local ac,ad=ui.mouse_position()local ae=client.key_state(0x01)if ae then for O=#ab,1,-1 do local H=ab[O]if H.visible and H:is_in_window(a1,a2)then H.first=true;if a3 then local af,ag=ac-a1,ad-a2;if H.position=="fixed"then local ah=H.left==nil and"right"or"left"local ai=H.top==nil and"bottom"or"top"local aj={{ah,(ah=="right"and-1 or 1)*af},{ai,(ai=="bottom"and-1 or 1)*ag}}for O=1,#aj do local ak,al=unpack(aj[O])local am=type(H[ak])if am=="string"and H[ak]:sub(-1,-1)=="%"then elseif am=="number"then H[ak]=H[ak]+al end end else H.x=H.x+af;H.y=H.y+ag end end;break end end end;a1,a2=ac,ad;a3=ae end;for O=1,#ab do local H=ab[O]if H.visible and H.in_menu==a5 then G(H)if H.paint_callback~=nil then H:paint_callback()end end end end;local a1,a2,a3;client.delay_call(0,client.set_event_callback,"paint",function()a4(false)end)client.delay_call(0,client.set_event_callback,"paint_ui",function()a4(true)end)return a end)()).new("watermark")
window.title = "Watermark"
window.title_bar = false
window.margin_bottom = 2
window.margin_left = 3
window.margin_right = 3
window.border_width = 2
window.top = 15
window.right = 15
window.in_menu = true

-- custom name
local db = database.read("sapphyrus_watermark") or {}

--local pingspike_reference = ui_reference("MISC", "Miscellaneous", "Ping spike")
local antiut_reference = ui_reference("MISC", "Settings", "Anti-untrusted")
local is_beta = pcall(ui_reference, "MISC", "Settings", "Crash logs")

local names = {"Logo", "Custom text", "FPS", "Ping", "KDR", "Server info", "Server framerate", "Server IP", "Network lag", "Tickrate", "Velocity", "Time", "Time + seconds"}

local watermark_reference = ui_new_multiselect("VISUALS", "Effects", "Watermark ", names)
local color_reference = ui_new_color_picker("VISUALS", "Effects", "Watermark", 149, 184, 6, 255)
local custom_name_reference = ui_new_textbox("VISUALS", "Effects", "Watermark name")
local rainbow_header_reference = ui_new_checkbox("VISUALS", "Effects", "Watermark rainbow header")

local fps_prev = 0
local value_prev = {}
local last_update_time = 0

local offset_x, offset_y = -15, 15
--local offset_x, offset_y = 525, 915 --debug, show above net_graph

local function clamp(cur_val, min_val, max_val)
    return math_min(math.max(cur_val, min_val), max_val)
end

local function lerp(a, b, percentage)
    return a + (b - a) * percentage
end

local function table_contains(tbl, val)
    for i=1, #tbl do
        if tbl[i] == val then
            return true
        end
    end
    return false
end

local function table_remove_element(tbl, val)
    local tbl_new = {}
    for i=1, #tbl do
        if tbl[i] ~= val then
            table_insert(tbl_new, tbl[i])
        end
    end
    return tbl_new
end

local function table_lerp(a, b, percentage)
    local result = {}
    for i=1, #a do
        result[i] = lerp(a[i], b[i], percentage)
    end
    return result
end

local function on_watermark_changed()
    local value = ui_get(watermark_reference)

    if #value > 0 then
        --Make Time / Time + seconds act as a kind of "switch", only allow one to be selected at a time.
        if table_contains(value, "Time") and table_contains(value, "Time + seconds") then
            local value_new = value
            if not table_contains(value_prev, "Time") then
                value_new = table_remove_element(value_new, "Time + seconds")
            elseif not table_contains(value_prev, "Time + seconds") then
                value_new = table_remove_element(value_new, "Time")
            end

            --this shouldn't happen, but why not add a failsafe
            if table_contains(value_new, "Time") and table_contains(value_new, "Time + seconds") then
                value_new = table_remove_element(value_new, "Time")
            end

            ui_set(watermark_reference, value_new)
            on_watermark_changed()
            return
        end
    end
    ui_set_visible(custom_name_reference, table_contains(value, "Custom text"))
    ui_set_visible(rainbow_header_reference, #value > 0)

    value_prev = value
end
ui_set_callback(watermark_reference, on_watermark_changed)
on_watermark_changed()

local function round(num, numDecimalPlaces)
    local mult = 10^(numDecimalPlaces or 0)
    return math_floor(num * mult + 0.5) / mult
end

local ft_prev = 0
local function get_fps()
    ft_prev = ft_prev * 0.9 + globals_absoluteframetime() * 0.1
    return round(1 / ft_prev)
end

local function lerp_color_yellow_red(val, max_normal, max_yellow, max_red, default, yellow, red)
    default = default or {255, 255, 255}
    yellow = yellow or {230, 210, 40}
    red = red or {255, 32, 32}
    if val > max_yellow then
        return unpack(table_lerp(yellow, red, clamp((val-max_yellow)/(max_red-max_yellow), 0, 1)))
    else
        return unpack(table_lerp(default, yellow, clamp((val-max_normal)/(max_yellow-max_normal), 0, 1)))
    end
end

local watermark_items = {
    {
        --gamesense logo
        name = "Logo",
        get_width = function(self, frame_data)
            self.game_width = renderer_measure_text(nil, "game")
            self.sense_width = renderer_measure_text(nil, "sense")
            self.beta_width = (is_beta and (renderer_measure_text(nil, " [beta]")) or 0)
            return self.game_width + self.sense_width + self.beta_width
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            local r_sense, g_sense, b_sense = ui_get(color_reference)

            renderer_text(x, y, 255, 255, 255, a, nil, 0, "game")
            renderer_text(x+self.game_width, y, r_sense, g_sense, b_sense, a, nil, 0, "sense")
            if is_beta then
                renderer_text(x+self.game_width+self.sense_width, y, 255, 255, 255, a*0.9, nil, 0, " [beta]")
            end
        end
    },
    {
        name = "Custom text",
        get_width = function(self, frame_data)
            local edit = ui_get(custom_name_reference)
            if edit ~= self.edit_prev and self.edit_prev ~= nil then
                db.custom_name = edit
            elseif edit == "" and db.custom_name ~= nil then
                ui_set(custom_name_reference, db.custom_name)
            end
            self.edit_prev = edit

            local text = db.custom_name
            if text ~= nil and text:gsub(" ", "") ~= "" then
                self.text = text
                return renderer_measure_text(nil, text)
            else
                self.text = nil
            end
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            -- local r_sense, g_sense, b_sense = ui_get(color_reference)
            renderer_text(x, y, r, g, b, a, nil, 0, self.text)
        end
    },
    {
        name = "FPS",
        get_width = function(self, frame_data)
            self.fps = get_fps()
            self.text = tostring(self.fps or 0) .. " fps"

            local fps_max, fps_max_menu = cvar_fps_max:get_float(), cvar_fps_max_menu:get_float()
            local fps_max = globals.mapname() == nil and math.min(fps_max == 0 and 999 or fps_max, fps_max_menu == 0 and 999 or fps_max) or fps_max == 0 and 999 or fps_max

            self.width = math.max(renderer_measure_text(nil, self.text), renderer_measure_text(nil, fps_max .. " fps"))
            return self.width
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            --fps
            local fps_r, fps_g, fps_b = r, g, b
            if self.fps < (1 / globals_tickinterval()) then
                -- fps_r, fps_g, fps_b = 255, 0, 0
            end

            renderer_text(x+self.width, y, fps_r, fps_g, fps_b, a, "r", 0, self.text)
        end
    },
    {
        name = "Ping",
        get_width = function(self, frame_data)
            local ping = client_latency()
            if ping > 0 then
                self.ping = ping
                self.text = round(self.ping*1000, 0) .. "ms"
                self.width = math.max(renderer_measure_text(nil, "999ms"), renderer_measure_text(nil, self.text))
                return self.width
            end
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            if self.ping > 0.15 then
                r, g, b = 255, 0, 0
            end
            renderer_text(x+self.width, y, r, g, b, a, "r", 0, self.text)
        end
    },
    {
        name = "KDR",
        get_width = function(self, frame_data)
            frame_data.local_player = frame_data.local_player or entity.get_local_player()
            if frame_data.local_player == nil then return end

            local player_resource = entity_get_player_resource()
            if player_resource == nil then return end

            self.kills = entity_get_prop(player_resource, "m_iKills", frame_data.local_player)
            self.deaths = math.max(entity_get_prop(player_resource, "m_iDeaths", frame_data.local_player), 1)

            self.kdr = self.kills/self.deaths

            if self.kdr ~= 0 then
                self.text = string.format("%1.1f", round(self.kdr, 1))
                self.text_width = math.max(renderer_measure_text(nil, "10.0"), renderer_measure_text(nil, self.text))
                self.unit_width = renderer_measure_text("-", "kdr")
                return self.text_width+self.unit_width
            end
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            renderer_text(x+self.text_width, y, r, g, b, a, "r", 0, self.text)
            renderer_text(x+self.text_width+self.unit_width, y+2, 255, 255, 255, a*0.75, "r-", 0, "kdr")
        end
    },
    {
        name = "Velocity",
        get_width = function(self, frame_data)
            frame_data.local_player = frame_data.local_player or entity.get_local_player()
            if frame_data.local_player == nil then return end

            local vel_x, vel_y = entity_get_prop(frame_data.local_player, "m_vecVelocity")
            if vel_x ~= nil then
                self.velocity = math_sqrt(vel_x*vel_x + vel_y*vel_y)

                self.vel_width = renderer_measure_text(nil, "9999")
                self.unit_width = renderer_measure_text("-", "vel")
                return self.vel_width+self.unit_width
            end
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            local velocity = self.velocity
            -- velocity = string.rep(round(globals.realtime() % 9, 0), 4)
            velocity = math_min(9999, velocity) + 0.4
            velocity = round(velocity, 0)

            renderer_text(x+self.vel_width, y, 255, 255, 255, a, "r", 0, velocity)
            renderer_text(x+self.vel_width+self.unit_width, y+3, 255, 255, 255, a*0.75, "r-", 0, "vel")
        end
    },
    {
        name = "Server framerate",
        get_width = function(self, frame_data)
            if not allow_unsafe_scripts then return end

            frame_data.local_player = frame_data.local_player or entity.get_local_player()
            if frame_data.local_player == nil then return end

            frame_data.net_channel_info = frame_data.net_channel_info or native_GetNetChannelInfo()
            if frame_data.net_channel_info == nil then return end

            local frame_time, frame_time_std_dev, frame_time_start_time_std_dev = GetRemoteFramerate(frame_data.net_channel_info)
            if frame_time ~= nil then
                self.framerate = frame_time * 1000
                self.var = frame_time_std_dev * 1000

                self.text1 = "sv:"
                self.text2 = string.format("%.1f", self.framerate)
                self.text3 = " +-"
                self.text4 = string.format("%.1f", self.var)

                self.width1 = renderer_measure_text(nil, self.text1)
                self.width2 = math.max(renderer_measure_text(nil, self.text2), renderer_measure_text(nil, "99.9"))
                self.width3 = renderer_measure_text(nil, self.text3)
                self.width4 = math.max(renderer_measure_text(nil, self.text4), renderer_measure_text(nil, "9.9"))

                return self.width1 + self.width2 + self.width3 + self.width4
            end
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            local fr_r, fr_g, fr_b = lerp_color_yellow_red(self.framerate, 8, 14, 20, {r, g, b})
            local vr_r, vr_g, vr_b = lerp_color_yellow_red(self.var, 5, 10, 18, {r, g, b})

            renderer_text(x, y, r, g, b, a, nil, 0, self.text1)
            renderer_text(x+self.width1+self.width2, y, fr_r, fr_g, fr_b, a, "r", 0, self.text2)
            renderer_text(x+self.width1+self.width2, y, r, g, b, a, nil, 0, self.text3)
            renderer_text(x+self.width1+self.width2+self.width3, y, vr_r, vr_g, vr_b, a, nil, 0, self.text4)
        end
    },
    {
        name = "Network lag",
        get_width = function(self, frame_data)
            if not allow_unsafe_scripts then return end

            frame_data.local_player = frame_data.local_player or entity.get_local_player()
            if frame_data.local_player == nil then return end

            frame_data.net_channel_info = frame_data.net_channel_info or native_GetNetChannelInfo()
            if frame_data.net_channel_info == nil then return end

            local reasons = {}

            -- timeout
            local time_since_last_received = native_GetTimeSinceLastReceived(frame_data.net_channel_info)
            if time_since_last_received ~= nil and time_since_last_received > 0.1 then
                table_insert(reasons, string_format("%.1fs timeout", time_since_last_received))
            end

            -- loss
            local avg_loss = native_GetAvgLoss(frame_data.net_channel_info, FLOW_INCOMING)
            if avg_loss ~= nil and avg_loss > 0 then
                table_insert(reasons, string_format("%d%% loss", math.ceil(avg_loss*100)))
            end

            -- choke
            local avg_choke = native_GetAvgChoke(frame_data.net_channel_info, FLOW_INCOMING)
            if avg_choke > 0 then
                table_insert(reasons, string_format("%d%% choke", math.ceil(avg_choke*100)))
            end

            if #reasons > 0 then
                self.text = table.concat(reasons, ", ")
                return renderer_measure_text(nil, self.text)
            end
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            renderer_text(x, y, 255, 32, 32, a, nil, 0, self.text)
        end
    },
    {
        name = "Server info",
        get_width = function(self, frame_data)
            if not allow_unsafe_scripts then return end

            frame_data.local_player = frame_data.local_player or entity.get_local_player()
            if frame_data.local_player == nil then return end

            frame_data.net_channel_info = frame_data.net_channel_info or native_GetNetChannelInfo()
            if frame_data.net_channel_info == nil then return end
            frame_data.is_loopback = frame_data.is_loopback == nil and native_IsLoopback(frame_data.net_channel_info) or frame_data.is_loopback

            local game_rules = entity.get_game_rules()
            frame_data.is_valve_ds = frame_data.is_valve_ds == nil and entity.get_prop(game_rules, "m_bIsValveDS") == 1 or frame_data.is_valve_ds

            local text
            if frame_data.is_loopback then
                text = "Local server"
            elseif frame_data.is_valve_ds then
                local game_mode_name
                local game_mode, game_type = cvar_game_mode:get_int(), cvar_game_type:get_int()

                local is_queued_matchmaking = entity.get_prop(game_rules, "m_bIsQueuedMatchmaking") == 1

                if is_queued_matchmaking then
                    if game_type == 0 and game_mode == 1 then
                        game_mode_name = "MM"
                    elseif game_type == 0 and game_mode == 2 then
                        game_mode_name = "Wingman"
                    elseif game_type == 3 then
                        game_mode_name = "Custom"
                    elseif game_type == 4 and game_mode == 0 then
                        game_mode_name = "Guardian"
                    elseif game_type == 4 and game_mode == 1 then
                        game_mode_name = "Co-op Strike"
                    elseif game_type == 6 and game_mode == 0 then
                        game_mode_name = "Danger Zone"
                    end
                else
                    if game_type == 0 and game_mode == 0 then
                        game_mode_name = "Casual"
                    elseif game_type == 1 and game_mode == 0 then
                        game_mode_name = "Arms Race"
                    elseif game_type == 1 and game_mode == 1 then
                        game_mode_name = "Demolition"
                    elseif game_type == 1 and game_mode == 2 then
                        game_mode_name = "Deathmatch"
                    end
                end

                if game_mode_name ~= nil then
                    text = "Valve (" .. game_mode_name .. ")"
                else
                    text = "Valve"
                end
            end

            if text ~= nil then
                self.text = text
                return renderer_measure_text(nil, text)
            end
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            renderer_text(x, y, 255, 255, 255, a, nil, 0, self.text)
        end
    },
    {
        name = "Server IP",
        get_width = function(self, frame_data)
            if not allow_unsafe_scripts then return end

            frame_data.net_channel_info = frame_data.net_channel_info or native_GetNetChannelInfo()
            if frame_data.net_channel_info == nil then return end

            frame_data.is_loopback = frame_data.is_loopback == nil and native_IsLoopback(frame_data.net_channel_info) or frame_data.is_loopback
            if frame_data.is_loopback then return end

            frame_data.is_valve_ds = frame_data.is_valve_ds == nil and entity.get_prop(entity.get_game_rules(), "m_bIsValveDS") == 1 or frame_data.is_valve_ds
            if frame_data.is_valve_ds then return end

            frame_data.server_address = frame_data.server_address or GetAddress(frame_data.net_channel_info)
            if frame_data.server_address ~= nil and frame_data.server_address ~= "" then
                self.text = frame_data.server_address
                return renderer_measure_text(nil, self.text)
            end
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            renderer_text(x, y, 255, 255, 255, a, nil, 0, self.text)
        end
    },
    {
        name = "Tickrate",
        get_width = function(self, frame_data)
            if globals.mapname() == nil then return end

            local tickinterval = globals_tickinterval()
            if tickinterval ~= nil then
                local text = 1/globals_tickinterval() .. " tick"
                self.text = text
                return renderer_measure_text(nil, text)
            end
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            renderer_text(x, y, 255, 255, 255, a, nil, 0, self.text)
        end
    },
    {
        name = "Time",
        get_width = function(self, frame_data)
            self.time_width = renderer_measure_text(nil, "00")
            self.sep_width = renderer_measure_text(nil, ":")
            return self.time_width + self.sep_width + self.time_width + (self.seconds and (self.sep_width + self.time_width) or 0)
        end,
        draw = function(self, x, y, w, h, r, g, b, a)
            -- local time_center = x + 16

            local hours, minutes, seconds, milliseconds = client_system_time()
            hours, minutes = string_format("%02d", hours), string_format("%02d", minutes)
            -- renderer_text(time_center, y, 255, 255, 255, a, "r", 0, hours)
            -- renderer_text(time_center, y, 255, 255, 255, a, "", 0, ":")
            -- renderer_text(time_center+4, y, 255, 255, 255, a, "", 0, minutes)

            -- time_center = time_center + 18

            -- if self.seconds then
            --     seconds = string_format("%02d", seconds)
            --     renderer_text(time_center, y, 255, 255, 255, a, "", 0, ":")
            --     renderer_text(time_center+4, y, 255, 255, 255, a, "", 0, seconds)
            -- end

            renderer_text(x, y, 255, 255, 255, a, "", 0, hours)
            renderer_text(x+self.time_width, y, 255, 255, 255, a, "", 0, ":")
            renderer_text(x+self.time_width+self.sep_width, y, 255, 255, 255, a, "", 0, minutes)

            if self.seconds then
                seconds = string_format("%02d", seconds)
                renderer_text(x+self.time_width*2+self.sep_width, y, 255, 255, 255, a, "", 0, ":")
                renderer_text(x+self.time_width*2+self.sep_width*2, y, 255, 255, 255, a, "", 0, seconds)
            end

        end,
        seconds = false
    },
}

local items_drawn = {}
window.pre_paint_callback = function()
    table_clear(items_drawn)
    local value = ui_get(watermark_reference)

    if table_contains(value, "Custom text") then
        value = table_remove_element(value, "Custom text")
        if table_contains(value, "Logo") then
            table_insert(value, 2, "Custom text")
        else
            table_insert(value, 1, "Custom text")
        end
    end

    local screen_width, screen_height = client_screen_size()
    local x = offset_x >= 0 and offset_x or screen_width + offset_x
    local y = offset_y >= 0 and offset_y or screen_height + offset_y

    for i=1, #watermark_items do
        local item = watermark_items[i]
        if item.name == "Time" then
            item.seconds = table_contains(value, "Time + seconds")

            if item.seconds then
                table_insert(value, "Time")
            end
        end
    end

    --calculate width and draw container
    local item_margin = 9
    local width = 0

    local frame_data = {}

    for i=1, #watermark_items do
        local item = watermark_items[i]
        if table_contains(value, item.name) then
            local item_width = item:get_width(frame_data)
            if item_width ~= nil and item_width > 0 then
                table.insert(items_drawn, {
                    item = item,
                    item_width = item_width,
                    x = width
                })
                width = width + item_width + item_margin
            end
        end
    end

    local _, height = renderer_measure_text(nil, "A")

    window.gradient_bar = ui_get(rainbow_header_reference)

    window:set_inner_width(width-item_margin)
    window:set_inner_height(height)

    window.visible = #items_drawn > 0
end

window.paint_callback = function()
    local r, g, b = 255, 255, 255
    local a_text = 230
    for i=1, #items_drawn do
        local item = items_drawn[i]

        -- bounding box
        -- renderer_rectangle(x_text+item.x, y_text, item.item_width, 14, 255, 0, 0, 100)

        -- draw item
        item.item:draw(window.i_x+item.x, window.i_y, item.item_width, 30, r, g, b, a_text)

        -- draw seperator
        if #items_drawn > i then
            renderer.rectangle(window.i_x+item.x+item.item_width+4, window.i_y+1, 1, window.i_h-1, 210, 210, 210, 255)
        end
    end
end

client.set_event_callback("shutdown", function()
    database.write("sapphyrus_watermark", db)
end)
 
Сверху Снизу