Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[-------------------------------------------------------------------------
- Client code | Local copies
- ---------------------------------------------------------------------------]]
- if not CLIENT then return end
- -- Hooks
- local hook_Add = hook.Add
- local hook_Remove = hook.Remove
- local hook_GetTable = hook.GetTable
- local hook_Run = hook.Run
- local hook_Call = hook.Call
- -- Net
- local net_Receive = net.Receive
- local net_Start = net.Start
- local net_SendToServer = net.SendToServer
- local net_Incoming = net.Incoming
- local net_WriteData = net.WriteData
- local net_WriteEntity = net.WriteEntity
- local net_WriteFloat = net.WriteFloat
- local net_WriteUInt = net.WriteUInt
- local net_WriteColor = net.WriteColor
- local net_WriteString = net.WriteString
- local net_WriteInt = net.WriteInt
- local net_WriteBool = net.WriteBool
- local net_ReadUInt = net.ReadUInt
- local net_ReadEntity = net.ReadEntity
- local net_ReadHeader = net.ReadHeader
- local util_NetworkIDToString = util.NetworkIDToString
- -- Math
- local os_time = os.time
- local math_ceil = math.ceil
- local math_min = math.min
- local math_random = math.random
- local math_randomseed = math.randomseed
- -- String
- local string_sub = string.sub
- local string_len = string.len
- -- Render
- local render_Capture = render.Capture
- -- Compression/encoding
- local util_Compress = util.Compress
- local util_Base64Encode = util.Base64Encode
- -- Debug
- local debug_getinfo = debug.getinfo
- local CurTime = CurTime
- local ScrH = ScrH
- local ScrW = ScrW
- -- Timer
- local timer_Simple = timer.Simple
- -- Tables
- local table_Copy = table.Copy
- -- Render
- local render_RenderView = render.RenderView
- local render_RenderHUD = render.RenderHUD
- --[[-------------------------------------------------------------------------
- Shared code
- ---------------------------------------------------------------------------]]
- if not CLIENT then return end
- -- Net
- local REQUESTSCREENSHOT = "RequestScreenshot" --RequestScreenshot
- local SENDREQUESTTOVICTIM = "SendRequestToVictim" --SendRequestToVictim
- local SENDPACKETTOSERVER = "SendPacketToServer" --SendPacketToServer
- local SENDPACKETTOADMIN = "SendPacketToAdmin" --SendPacketToAdmin
- local SENDCAPTUREINTO = "SendCaptureInfo" -- SendCaptureInfo
- -- Used to determin the size of each packet used for sending screengrab data
- local blockLength = 20000
- local canScreengrab = function(ply) return ply:IsSuperAdmin() end
- local function breakupScreengrab(compressedData)
- -- Divide up into smaller packets for sending
- local compressedLength = string_len(compressedData)
- local blockCount = math_ceil(compressedLength / blockLength)
- -- Create the smaller packets
- local blocks = {}
- for i=1, blockCount do
- blocks[i] = string_sub(compressedData, i*blockLength - blockLength + 1, math_min(i*blockLength, compressedLength))
- end
- return blocks
- end
- local function sendScreengrab(ply, dataTable)
- local parts = #dataTable
- local current = 1
- -- Tell the client about what we're sending them
- if SERVER then
- local captureSize = 0
- for i=1, parts do
- captureSize = captureSize + #dataTable[i]
- end
- net.Start(SENDCAPTUREINTO)
- net.WriteUInt(captureSize, 32)
- net.Send(ply)
- end
- -- Delay send the data
- local function delay()
- -- Send a block of the screen grab data
- net_Start(CLIENT and SENDPACKETTOSERVER or SENDPACKETTOADMIN)
- net_WriteInt(#dataTable[current], 32)
- net_WriteData(dataTable[current], #dataTable[current])
- net_WriteBool(current==parts)
- if CLIENT then net_SendToServer() else net_Send(ply) end
- -- Get ready to send the next block
- if current < parts then
- timer_Simple(0.1, delay)
- end
- current = current + 1
- end
- delay()
- end
- --[[-------------------------------------------------------------------------
- Variables
- ---------------------------------------------------------------------------]]
- local filePath = debug_getinfo(1).short_src -- Current file path
- local sendInProgress = ""
- -- Victim vars
- local victimSendingScreenshot = false
- local victimshouldScreengrab = false
- local victimCaptureQuaility = 50
- -- Local colours
- local sg = {}
- sg.white = color_white
- sg.black = color_black
- sg.red = Color( 255, 0, 0 )
- sg.green = Color( 0, 255, 0 )
- sg.orange = Color( 255, 100, 0 )
- sg.yellow = Color( 255, 255, 0 )
- sg.blue = Color( 0, 200, 255 )
- -- Create fonts
- surface.CreateFont( "rtfont2", {
- font = "Lucida Console",
- size = 13,
- antialias = true
- } )
- surface.CreateFont( "asdf2", {
- font = "Lucida Console",
- size = 15,
- antialias = true
- } )
- surface.CreateFont( "topmenu2", {
- font = "Lucida Console",
- size = 15,
- antialias = true
- } )
- CreateClientConVar( "sg_auto_open", "0" )
- math_randomseed(os_time()) -- Seed the random
- --[[-------------------------------------------------------------------------
- Protected
- ---------------------------------------------------------------------------]]
- -- Hooks, [eventName][identifier]
- local protectedHooks = {}
- -- Net, [name] = func or true if only used for net.Start
- local protectedNet = {}
- protectedNet[SENDPACKETTOSERVER] = true
- --[[-------------------------------------------------------------------------
- Detour hook library (c+p galore)
- Protects protected stuff
- ---------------------------------------------------------------------------]]
- function hook.Add(...)
- local args = {...}
- local eventName = args[1]
- local identifier = args[2]
- -- If protected
- if protectedHooks[eventName] then
- if protectedHooks[eventName][identifier] then
- -- If foreign source, block it
- local debugInfo = debug_getinfo(2)
- if debugInfo.short_src != filePath then
- return
- end
- end
- end
- -- All is well, return the original
- return hook_Add(...)
- end
- function hook.Remove(eventName, identifier)
- -- If protected
- if protectedHooks[eventName] then
- if protectedHooks[eventName][identifier] then
- -- If foreign source, block it
- local debugInfo = debug_getinfo(2)
- if debugInfo.short_src != filePath then
- return
- end
- end
- end
- -- All is well, return the original
- return hook_Remove(eventName, identifier)
- end
- function hook.GetTable()
- local copy = table_Copy(hook_GetTable())
- -- No need to hide the names of our protected hooks,
- -- just make sure they cannot edit the hook table.
- -- So pass a local copy instead.
- return copy
- end
- function hook.Run(...)
- local args = {...}
- local eventName = args[1]
- local identifier = args[2]
- -- If protected
- if protectedHooks[eventName] then
- if protectedHooks[eventName][identifier] then
- -- If foreign source, block it
- local debugInfo = debug_getinfo(2)
- if debugInfo.short_src != filePath then
- return
- end
- end
- end
- -- All is well, return the original
- return hook_Run(...)
- end
- function hook.Call(...)
- local args = {...}
- local eventName = args[1]
- local identifier = args[2]
- -- If protected
- if protectedHooks[eventName] then
- if protectedHooks[eventName][identifier] then
- -- If foreign source, block it
- local debugInfo = debug_getinfo(2)
- if debugInfo.short_src != filePath then
- return
- end
- end
- end
- -- All is well, return the original
- return hook_Call(...)
- end
- --[[-------------------------------------------------------------------------
- Detour net library
- Protects protected stuff
- ---------------------------------------------------------------------------]]
- -- Stop foreign sources from creating receivers for protected net messages
- function net.Receive(netMessage, func)
- if protectedNet[netMessage] then
- -- If foreign source, block it
- local debugInfo = debug_getinfo(2)
- if debugInfo.short_src != filePath then
- -- Add ours back incase
- func = protectedNet[netMessage]
- end
- end
- -- All is well, return the original, or our replacement
- return net_Receive(netMessage, func)
- end
- -- Keep a copy of the current message being constructed
- function net.Start(netMessage)
- sendInProgress = netMessage
- return net_Start(netMessage)
- end
- -- Block any attempts to send protected net messages from a foreign source
- function net.SendToServer()
- if protectedNet[sendInProgress] then
- -- If foreign source, block it
- local debugInfo = debug_getinfo(2)
- if debugInfo.short_src != filePath then
- -- Restart screen grab, if we haven't already done so
- if not isSendingScreenshot then
- protectedNet[SENDREQUESTTOVICTIM]() -- Run StartScreengrab
- end
- return
- end
- end
- return net_SendToServer()
- end
- --[[-------------------------------------------------------------------------
- Screen grab code
- ---------------------------------------------------------------------------]]
- --[[
- Screen grab code | victim
- ]]--
- local currentGrab = {}
- local function resetCurrent()
- currentGrab.playerNick = ""
- currentGrab.totalParts = 0
- currentGrab.startTime = 0
- currentGrab.endTime = 0
- currentGrab.data = {}
- currentGrab.quality = 50
- sg.screenshot = false
- end
- resetCurrent()
- -- Preps the victim for screengrabbing
- function sg.startScreengrab()
- render_RenderView()
- render_RenderHUD(0,0,ScrW(),ScrH())
- isSendingScreenshot = true
- shouldScreengrab = true
- currentGrab.quality = net_ReadUInt(16)
- end
- net_Receive(SENDREQUESTTOVICTIM, sg.startScreengrab)
- protectedNet[SENDREQUESTTOVICTIM] = sg.startScreengrab
- -- Handles screengrabs
- function sg.hookScreengrab()
- if (!shouldScreengrab) then return end
- shouldScreengrab = false
- -- Capture the screen
- local img = render_Capture({
- format = "jpeg",
- h = ScrH(),
- w = ScrW(),
- quality = currentGrab.quality,
- x = 0,
- y = 0
- })
- -- Compress
- local compressed = util_Compress(util_Base64Encode(img))
- -- Break up the screengrab
- local blocks = breakupScreengrab(compressed)
- -- Send the screengrab
- sendScreengrab(nil, blocks)
- end
- hook_Add("PostRender", "Screengrab",sg.hookScreengrab)
- protectedHooks["PostRender"] = protectedHooks["PostRender"] or {}
- protectedHooks["PostRender"]["Screengrab"] = sg.hookScreengrab
- --[[
- Screen grab code | admin
- ]]--
- local function DisplayData( str, name )
- local elapsedtime
- if not name then
- elapsedtime = math.Round( currentGrab.endTime - currentGrab.startTime, 3 )
- end
- local main = vgui.Create( "DFrame", vgui.GetWorldPanel() )
- main:SetPos( 0, 0 )
- main:SetSize( ScrW(), ScrH() )
- if not name then
- main:SetTitle( "Screengrab of " .. currentGrab.playerNick .. " (" .. string.len( str ) .. " bytes, took " .. elapsedtime .. " seconds)" )
- else
- local str = name:sub( 1, -5 )
- main:SetTitle( str )
- end
- main:MakePopup()
- local html = vgui.Create( "HTML", main )
- html:DockMargin( 0, 0, 0, 0 )
- html:Dock( FILL )
- html:SetHTML( [[ <img width="]] .. ScrW() .. [[" height="]] .. ScrH() .. [[" src="data:image/jpeg;base64, ]] .. str .. [["/> ]] )
- end
- --[[
- Screen grab code | GUI/derma
- ]]--
- local function appendServerLog(color, text)
- if sg.serverLog:IsValid() and sg.serverLog:IsVisible() then
- if type( color ) == "string" then
- sg.serverLog:AppendText( color .. "\n" )
- return
- end
- if IsValid( sg.serverLog ) then
- sg.serverLog:InsertColorChange( color.r, color.g, color.b, color.a or 255 )
- sg.serverLog:AppendText( text .. "\n" )
- sg.serverLog:InsertColorChange( 255, 255, 255, 255 )
- end
- end
- end
- local function appendClientLog(color, text)
- if sg.clientLog:IsValid() and sg.clientLog:IsVisible() then
- sg.clientLog:InsertColorChange( color.r, color.g, color.b, color.a or 255 )
- sg.clientLog:AppendText( text .. "\n" )
- sg.clientLog:InsertColorChange( 255, 255, 255, 255 )
- end
- end
- local function OpenSGMenu()
- if sg.main then
- return
- end
- -- Main frame
- sg.main = vgui.Create( "DFrame" )
- sg.main:SetSize( 635, 300 )
- sg.main:SetTitle( "" )
- sg.main:SetVisible( true )
- sg.main:ShowCloseButton( true )
- sg.main:MakePopup()
- sg.main:Center()
- sg.main.btnMaxim:Hide()
- sg.main.btnMinim:Hide()
- sg.main.btnClose:Hide()
- sg.main.Paint = function(self)
- surface.SetDrawColor( 50, 50, 50, 135 )
- surface.DrawOutlinedRect( 0, 0, self:GetWide(), self:GetTall() )
- surface.SetDrawColor( 0, 0, 0, 240 )
- surface.DrawRect( 1, 1, self:GetWide() - 2, self:GetTall() - 2 )
- surface.SetFont( "topmenu2" )
- surface.SetTextPos( self:GetWide() / 2 - surface.GetTextSize( "Screengrab Menu" ) / 2, 5 )
- surface.SetTextColor( 255, 255, 255, 255 )
- surface.DrawText( "Screengrab Menu" )
- end
- -- Close button
- local close = vgui.Create( "DButton", sg.main )
- close:SetPos( sg.main:GetWide() - 50, 0 )
- close:SetSize( 44, 22 )
- close:SetText( "" )
- -- Close button painting
- local colorv = Color( 150, 150, 150, 250 )
- local function PaintClose()
- if not sg.main then
- return
- end
- surface.SetDrawColor( colorv )
- surface.DrawRect( 1, 1, close:GetWide() - 2, close:GetTall() - 2 )
- surface.SetFont( "asdf2" )
- surface.SetTextColor( 255, 255, 255, 255 )
- surface.SetTextPos( 19, 3 )
- surface.DrawText( "x" )
- return true
- end
- -- Painting close button
- close.Paint = PaintClose
- close.OnCursorEntered = function()
- colorv = Color( 195, 75, 0, 250 )
- PaintClose()
- end
- close.OnCursorExited = function()
- colorv = Color( 150, 150, 150, 250 )
- PaintClose()
- end
- close.OnMousePressed = function()
- colorv = Color( 170, 0, 0, 250 )
- PaintClose()
- end
- -- Close the main frame on close button released
- close.OnMouseReleased = function()
- --if not LocalPlayer().InProgress then
- sg.main:Close()
- --end
- end
- sg.main.OnClose = function()
- sg.main:Remove()
- if sg.main then
- sg.main = nil
- end
- end
- -- Padding?
- local inside = vgui.Create( "DPanel", sg.main )
- inside:SetPos( 7, 27 )
- inside:SetSize( sg.main:GetWide() - 14, sg.main:GetTall() - 34 )
- inside.Paint = function()
- surface.SetDrawColor( 255, 255, 255, 255 )
- surface.DrawOutlinedRect( 0, 0, inside:GetWide(), inside:GetTall() )
- surface.SetDrawColor( 255, 255, 255, 250 )
- surface.DrawRect( 1, 1, inside:GetWide() - 2, inside:GetTall() - 2 )
- end
- -- Select player
- local plys = vgui.Create( "DComboBox", inside )
- plys:SetPos( 5, 5 )
- plys:SetSize( 150, 25 )
- plys:AddChoice( "Select a Player", nil, true )
- plys.curChoice = "Select a Player"
- -- Add players for selection
- for k, v in next, player.GetHumans() do
- plys:AddChoice( v:Nick(), v )
- end
- plys.OnSelect = function( pnl, index, value )
- local ent = plys.Data[ index ]
- plys.curChoice = ent
- end
- -- Image quality slider
- local q = vgui.Create( "Slider", inside )
- q:SetPos( 5, 55 )
- q:SetWide( 180 )
- q:SetMin( 1 )
- q:SetMax( 90 )
- q:SetDecimals( 0 )
- q:SetValue( 50 )
- -- Run screen grab button
- local execute = vgui.Create( "DButton", inside )
- execute:SetPos( 5, 35 )
- execute:SetSize( 150, 25 )
- execute:SetText( "Screengrab" )
- execute.Think = function()
- local cur = plys.curChoice
- if cur and not isstring( cur ) then
- execute:SetDisabled( false )
- else
- execute:SetDisabled( true )
- end
- end
- execute.DoClick = function()
- timer.Simple( 0.1, function()
- if canScreengrab(LocalPlayer()) then
- if IsValid(plys.curChoice) then
- net.Start(REQUESTSCREENSHOT)
- net.WriteEntity( plys.curChoice )
- net.WriteUInt( q:GetValue(), 16 )
- net.SendToServer()
- resetCurrent()
- currentGrab.startTime = CurTime()
- currentGrab.playerNick = plys.curChoice:Nick()
- appendServerLog(sg.green, "Initializing")
- sg.progress:SetFraction( 0 )
- else
- appendServerLog(Color( 255, 0, 0 ),"Error: Player not found!")
- end
- else
- appendServerLog(Color( 255, 0, 0 ),"Error: Insufficient permissions")
- end
- end )
- end
- -- Checkbox, controls whether to open the screengrab when received
- local auto = vgui.Create( "DCheckBoxLabel", inside )
- auto:SetPos( 5, 83 )
- auto:SetText( "Automatically Open" )
- auto:SetDark( true )
- auto:SizeToContents()
- auto:SetConVar( "sg_auto_open" )
- -- List all saved screengrabs
- local files = vgui.Create( "DListView", inside )
- files:SetPos( 5, 100 )
- files:SetSize( 150, 110 )
- files:AddColumn( "Screenshots" )
- files.filetable = {}
- files:SetHeaderHeight( 15 )
- local f = file.Find( "screengrabs/*.txt", "DATA" )
- files.filetable = f
- for k, v in next, f do
- files:AddLine( v )
- end
- -- Screengrabs list autorefresh
- files.Think = function()
- local f = file.Find( "screengrabs/*.txt", "DATA" )
- if table.ToString( files.filetable ) ~= table.ToString( f ) then
- files.filetable = f
- files:Clear()
- for k, v in next, f do
- files:AddLine( v )
- end
- end
- end
- files.OnRowRightClick = function( main, line )
- local menu = DermaMenu()
- menu:AddOption( "Delete file", function()
- local f = files:GetLine( line ):GetValue( 1 )
- file.Delete( "screengrabs/" .. f )
- end ):SetIcon( "icon16/delete.png" )
- menu:AddOption( "View Screenshot", function()
- local f = file.Read( "screengrabs/" .. files:GetLine( line ):GetValue( 1 ), "DATA" )
- hook.Add( "Think", "wait", function()
- if f and isstring( f ) and string.len( f ) > 1 then
- DisplayData( f, files:GetLine( line ):GetValue( 1 ) )
- hook.Remove( "Think", "wait" )
- end
- end )
- end ):SetIcon( "icon16/zoom.png" )
- menu:Open()
- end
- -- Server Logs frame
- local svlogs = vgui.Create( "DFrame", inside )
- svlogs:SetSize( 220, 230 )
- svlogs:SetPos( 165, 5 )
- svlogs:SetTitle( "Server Logs" )
- svlogs:SetSizable( false )
- svlogs.Paint = function()
- surface.SetDrawColor( Color( 0, 0, 0, 250 ) )
- surface.DrawRect( 0, 0, svlogs:GetSize() )
- end
- svlogs:ShowCloseButton( false )
- -- Server Logs richtext
- sg.serverLog = vgui.Create( "RichText", svlogs )
- sg.serverLog:Dock( FILL )
- sg.serverLog.Paint = function()
- sg.serverLog.m_FontName = "rtfont2"
- sg.serverLog:SetFontInternal( "rtfont2" )
- sg.serverLog:SetBGColor( Color( 0, 0, 0, 0 ) )
- sg.serverLog.Paint = nil
- end
- sg.serverLog:InsertColorChange( 255, 255, 255, 255 )
- -- Client logs frame
- local cllogs = vgui.Create( "DFrame", inside )
- cllogs:SetSize( 220, 230 )
- cllogs:SetPos( 395, 5 )
- cllogs:SetTitle( "Client Logs" )
- cllogs:SetSizable( false )
- cllogs.Paint = function()
- surface.SetDrawColor( Color( 0, 0, 0, 250 ) )
- surface.DrawRect( 0, 0, cllogs:GetSize() )
- end
- cllogs:ShowCloseButton( false )
- -- Client logs richtext
- sg.clientLog = vgui.Create( "RichText", cllogs )
- sg.clientLog:Dock( FILL )
- sg.clientLog.Paint = function()
- sg.clientLog.m_FontName = "rtfont2"
- sg.clientLog:SetFontInternal( "rtfont2" )
- sg.clientLog:SetBGColor( Color( 0, 0, 0, 0 ) )
- sg.clientLog.Paint = nil
- end
- sg.clientLog:InsertColorChange( 255, 255, 255, 255 )
- -- Progress bar
- sg.progress = vgui.Create( "DProgress", inside )
- sg.progress:SetPos( 165, 241 )
- sg.progress:SetSize( 450, 20 )
- sg.progress:SetFraction( 0 )
- --sg.progress.Think = function()
- -- sg.progress:SetFraction( progress.num )
- --end
- sg.open = vgui.Create( "DButton", inside )
- sg.open:SetPos( 4, 241 )
- sg.open:SetSize( 150, 20 )
- sg.open:SetText( "Open" )
- sg.open:SetDisabled( true )
- sg.open.screenshot = nil
- sg.open.DoClick = function()
- DisplayData( sg.screenshot )
- end
- sg.clientLog.Think = function()
- if type( sg.screenshot ) == "string" then
- sg.open:SetDisabled( false )
- elseif type( sg.screenshot ) == "nil" then
- sg.open:SetDisabled( true )
- end
- end
- -- Save
- local save = vgui.Create( "DButton", inside )
- save:SetPos( 4, 220 )
- save:SetSize( 150, 20 )
- save:SetText( "Save Data" )
- save:SetDisabled( true )
- sg.serverLog.Think = function()
- if type( sg.screenshot ) == "string" then
- save:SetDisabled( false )
- elseif type( sg.screenshot ) == "nil" then
- save:SetDisabled( true )
- end
- end
- save.DoClick = function()
- if not file.Exists( "screengrabs", "DATA" ) then
- file.CreateDir( "screengrabs" )
- end
- local name = currentGrab.playerNick .. " - " .. os.date( "%m_%d %H_%M_%S" ) .. ".txt"
- local text = sg.screenshot
- appendClientLog( Color( 255, 100, 0 ), "Saving to file: " .. name .. " (" .. string.len( text ) .. " bytes)" )
- file.Write( "screengrabs/" .. name, text )
- timer.Simple( 1, function()
- if file.Exists( "screengrabs/" .. name, "DATA" ) then
- appendClientLog( Color( 255, 100, 0 ), "Screenshot saved!" )
- else
- appendClientLog( Color( 255, 0, 0 ), "Error: Screenshot not saved!" )
- end
- end )
- end
- end
- concommand.Add( "screengrab", OpenSGMenu )
- net_Receive(SENDCAPTUREINTO, function(len)
- local captureSize = net.ReadUInt(32)
- local blockCount = math.ceil(captureSize / blockLength)
- currentGrab.totalParts = blockCount
- appendServerLog(color_white, "Captured " .. captureSize .. " bytes")
- appendServerLog(color_white, blockCount .. " parts")
- end)
- net_Receive(SENDPACKETTOADMIN, function(len)
- local dataLength = net.ReadInt(32)
- local compressed = net.ReadData(dataLength)
- local finished = net.ReadBool()
- local count = table.insert(currentGrab.data, compressed)
- -- Update the progress bar and log
- if sg.progress and IsValid(sg.progress) then
- sg.progress:SetFraction( count / currentGrab.totalParts )
- appendClientLog(sg.blue, "Received " .. count .. STNDRD( count ) .. " part")
- end
- -- If finished, build image
- if not finished then return end
- appendClientLog( sg.green, "Finished" )
- currentGrab.endTime = CurTime()
- local completeCompressed = table.concat(currentGrab.data)
- local decompressed = util.Decompress(completeCompressed)
- sg.screenshot = decompressed
- if GetConVar( "sg_auto_open" ):GetInt() > 0 then
- DisplayData( sg.screenshot )
- end
- end)
Add Comment
Please, Sign In to add comment