--------------------------------------------- -- BOTDETECTV3.LUA --------------------------------------------- -- covert-ops only, logging version + autokick version -- sten, scoped garand and scoped K43 -- FOREWORD: -- This script was never intended for release. However, once word of it got out, I have had repeated requests -- and so have responded. It is important to understand what this script does before using it so its limitations -- are clear. THIS SCRIPT DOES NOT DETECT AIMBOTS. What it does is look for consecutive kills with a limited number -- of weapons and measure the flux in damage_given (damage/time). The scipt assumes that the damage caused between -- 2 consecutive kills was inflicted by the MOD in et_obituary. This is a dangerous assumption, and so I have -- limited the application of the script to sten, scoped K43 and scoped Garand to increase the chances of it being -- true. Additionally, to help ensure that innocent players are never kicked, the script works on a "strike" system -- i.e. 3 strikes before the player is kicked. -- PURPOSE -- This script was written to act as an annoyance to botters who come on the server when no admins are around. It -- is no substitute for a good admin and will only ever kick botters using their bots on full. -- I collected data for ~1 month and analysed it in order to find flux_weapon limits that are not obtained by -- honest players (certainly never more than once in a map). If you use the limits here, it is unlikely an innocent -- player will ever be kicked, unless he is repeatedly against a number of afks. I deemed this to be infrequent -- enough to be acceptable. -- GhosT:McSteve -- www.ghostworks.co.uk -- #ghostworks, #pbbans @quakenet -- Version 3, 17/2/07 ----------------------------------------------------------------------------------- -- ADMIN - SET THE OPTIONS HERE strike_logging = 0 filename = "covert_logv3.txt" reason = "Client communication failure;;" -- You can change the reason to whatever you want. I would rather botters did not come back, hence I -- set the message to something that may influence the player to go elsewhere. ----------------------------------------------------------------------------------- ---------------------- DO NOT MODIFY BELOW THIS LINE ---------------------------- ----------------------------------------------------------------------------------- -- on game initialise, get the maxclients and initialise the arrays function et_InitGame( levelTime, randomSeed, restart ) --called on game initialise maxclients = tonumber( et.trap_Cvar_Get( "sv_maxClients" ) ) --gets the maxclients --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3runtime init\"\n") stenswitch = {} stentime = {} flux_sten = {} k43switch = {} k43time = {} flux_k43 = {} garandswitch = {} garandtime = {} flux_garand = {} damagegiven = {} strikes = {} for i = 0, (maxclients - 1) do stenswitch[i] = 0 stentime[i] = 0 flux_sten[i] = 0 k43switch[i] = 0 k43time[i] = 0 flux_k43[i] = 0 garandswitch[i] = 0 garandtime[i] = 0 flux_garand[i] = 0 damagegiven[i] = 0 strikes[i] = 0 end end -- prototype code here function et_Obituary( victim, killer, meansofdeath ) if et.gentity_get(killer, "sess.playerType") ~= 4 then return end -- is the player a covert ops? --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3runtime obituary\"\n") --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3mod ^4" .. meansofdeath .. "\"\n") -- if the stenner dies, set his stenswitch to 0 if stenswitch[victim] == 1 then stenswitch[victim] = 0 elseif k43switch[victim] == 1 then k43witch[victim] = 0 elseif garandswitch[victim] == 1 then garandswitch[victim] = 0 end -- if the kill was with a sten (11), then find the flux if meansofdeath == 11 then -- if it is the first kill, then capture initial time and damage if stenswitch[killer] == 0 then stenswitch[killer] = 1 stentime[killer] = et.trap_Milliseconds() damagegiven[killer] = et.gentity_get(killer, "sess.damage_given") --if there was a sten kill before it, capture 2nd time else temp_time = stentime[killer] --(from 1st kill) stentime[killer] = et.trap_Milliseconds() -- time for current kill d_stentime = (stentime[killer]) - temp_time -- time between the 2 kills temp_damagegiven = damagegiven[killer] -- from 1st kill damagegiven[killer] = et.gentity_get(killer, "sess.damage_given") --from current kill d_damagegiven = (damagegiven[killer]) - temp_damagegiven -- damage difference between the 2 kills -- calculate the damage/time flux flux_sten[killer] = d_damagegiven/d_stentime --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3flux_sten ^4" .. flux_sten[killer] .. "\"\n") -- max theoretical flux_sten = 333 hp/s ( = 0.333 hp/ms ) -- for 1v1, never really managed to get it above 0.2 -- if the flux is too high, then if (flux_sten[killer]) > (0.120) then strikes[killer] = 1 + strikes[killer] tempname = et.Info_ValueForKey( et.trap_GetUserinfo( killer ), "name" ) cnoname = et.Q_CleanStr(tempname) -- if strikes > 2 then perform action if strikes[killer] > 2 then --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"" .. tempname .. " ^3has been auto-kicked. \n") --command = "kick " .. killer --et.trap_SendConsoleCommand(et.EXEC_NOW, command) et.trap_DropClient( killer, reason ) strikes[killer] = 0 end --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3strikes ^4" .. strikes[killer] .. "\"\n") if strike_logging == 1 then --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3sten pre-logging\"\n") --tempuserinfo = et.Q_CleanStr((et.trap_GetUserinfo( cno )) --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3runtime " .. tempuserinfo .. "\"\n") info = os.date("%x %I:%M:%S%p") .. " ; " .. cnoname .. " ; flux_sten ; " .. flux_sten[killer] .. "\n" fd,len = et.trap_FS_FOpenFile(filename, et.FS_APPEND) count = et.trap_FS_Write(info, string.len(info), fd) et.trap_FS_FCloseFile(fd) fd = nil --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3sten post-logging\"\n") end end end elseif meansofdeath == 56 then -- 56 = MOD_K43_SCOPE -- if it is the first kill, then capture initial time and damage if k43switch[killer] == 0 then k43switch[killer] = 1 k43time[killer] = et.trap_Milliseconds() damagegiven[killer] = et.gentity_get(killer, "sess.damage_given") --if there was a k43 kill before it, capture 2nd time else temp_time = k43time[killer] --(from 1st kill) k43time[killer] = et.trap_Milliseconds() -- time for current kill d_k43time = (k43time[killer]) - temp_time -- time between the 2 kills temp_damagegiven = damagegiven[killer] -- from 1st kill damagegiven[killer] = et.gentity_get(killer, "sess.damage_given") --from current kill d_damagegiven = (damagegiven[killer]) - temp_damagegiven -- damage difference between the 2 kills -- calculate the damage/time flux flux_k43[killer] = d_damagegiven/d_k43time --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3flux_k43 ^4" .. flux_k43[killer] .. "\"\n") -- max theoretical flux_k43 = 250 hp/s ( = 0.250 hp/ms ) -- for 1v1, never really managed to get it above 0.2 -- if the flux is too high, then if (flux_k43[killer]) > (0.100) then strikes[killer] = 1 + strikes[killer] --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3strikes ^4" .. strikes[killer] .. "\"\n") tempname = et.Info_ValueForKey( et.trap_GetUserinfo( killer ), "name" ) cnoname = et.Q_CleanStr(tempname) -- if strikes > 2 then perform action if strikes[killer] > 2 then --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"" .. tempname .. " ^3has been auto-kicked. \n") --command = "kick " .. killer --et.trap_SendConsoleCommand(et.EXEC_NOW, command) et.trap_DropClient( killer, reason ) strikes[killer] = 0 end if strike_logging == 1 then --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3k43 pre-logging\"\n") cnoname = et.Q_CleanStr(et.Info_ValueForKey( et.trap_GetUserinfo( killer ), "name" )) --tempuserinfo = et.Q_CleanStr((et.trap_GetUserinfo( cno )) --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3runtime " .. tempuserinfo .. "\"\n") info = os.date("%x %I:%M:%S%p") .. " ; " .. cnoname .. " ; flux_k43 ; " .. flux_k43[killer] .. "\n" fd,len = et.trap_FS_FOpenFile(filename, et.FS_APPEND) count = et.trap_FS_Write(info, string.len(info), fd) et.trap_FS_FCloseFile(fd) fd = nil --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3k43 post-logging\"\n") end end end elseif meansofdeath == 51 then -- 51 = MOD_GARAND_SCOPE -- if it is the first kill, then capture initial time and damage if garandswitch[killer] == 0 then garandswitch[killer] = 1 garandtime[killer] = et.trap_Milliseconds() damagegiven[killer] = et.gentity_get(killer, "sess.damage_given") --if there was a garand kill before it, capture 2nd time else temp_time = garandtime[killer] --(from 1st kill) garandtime[killer] = et.trap_Milliseconds() -- time for current kill d_garandtime = (garandtime[killer]) - temp_time -- time between the 2 kills temp_damagegiven = damagegiven[killer] -- from 1st kill damagegiven[killer] = et.gentity_get(killer, "sess.damage_given") --from current kill d_damagegiven = (damagegiven[killer]) - temp_damagegiven -- damage difference between the 2 kills -- calculate the damage/time flux flux_garand[killer] = d_damagegiven/d_garandtime --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3flux_garand ^4" .. flux_garand[killer] .. "\"\n") -- max theoretical flux_garand = 250 hp/s ( = 0.250 hp/ms ) -- for 1v1, never really managed to get it above 0.2 -- if the flux is too high, then if (flux_garand[killer]) > (0.100) then strikes[killer] = 1 + strikes[killer] --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3strikes ^4" .. strikes[killer] .. "\"\n") tempname = et.Info_ValueForKey( et.trap_GetUserinfo( killer ), "name" ) cnoname = et.Q_CleanStr(tempname) -- if strikes > 2 then perform action if strikes[killer] > 2 then --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"" .. tempname .. " ^3has been auto-kicked. \n") --command = "kick " .. killer --et.trap_SendConsoleCommand(et.EXEC_NOW, command) et.trap_DropClient( killer, reason ) strikes[killer] = 0 end if strike_logging == 1 then --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3garand pre-logging\"\n") cnoname = et.Q_CleanStr(et.Info_ValueForKey( et.trap_GetUserinfo( killer ), "name" )) --tempuserinfo = et.Q_CleanStr((et.trap_GetUserinfo( cno )) --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3runtime " .. tempuserinfo .. "\"\n") info = os.date("%x %I:%M:%S%p") .. " ; " .. cnoname .. " ; flux_garand ; " .. flux_garand[killer] .. "\n" fd,len = et.trap_FS_FOpenFile(filename, et.FS_APPEND) count = et.trap_FS_Write(info, string.len(info), fd) et.trap_FS_FCloseFile(fd) fd = nil --et.trap_SendConsoleCommand(et.EXEC_APPEND, "qsay \"^3garand post-logging\"\n") end end end else -- if the kill was not with a sten, k43 or garand stenswitch[killer] = 0 k43switch[killer] = 0 garandswitch[killer] = 0 end end