319 lines
7.6 KiB
Lua
319 lines
7.6 KiB
Lua
local test = dbgTest or {}
|
||
dbgTest = test
|
||
|
||
test.config = {
|
||
id = 'dbg3',
|
||
attempts = 2,
|
||
}
|
||
|
||
local dbvar = 'quiz_' .. test.config.id
|
||
|
||
local storageApi = octolib.api({
|
||
url = 'https://dbgtest.octo.gg/api',
|
||
headers = { ['Authorization'] = CFG.keys.test },
|
||
})
|
||
|
||
local function tomorrow()
|
||
|
||
local dateTime = os.date('*t', os.time())
|
||
dateTime.hour = 0
|
||
dateTime.min = 0
|
||
dateTime.sec = 0
|
||
return os.time(dateTime) + 60 * 60 * 24
|
||
|
||
end
|
||
|
||
function test.getAttempts(ply)
|
||
|
||
local data = ply:GetDBVar(dbvar) or {}
|
||
return data[1] or test.config.attempts
|
||
|
||
end
|
||
|
||
function test.takeAttempt(ply)
|
||
|
||
local data = ply:GetDBVar(dbvar) or {}
|
||
ply:SetDBVar(dbvar, {
|
||
(data[1] or test.config.attempts) - 1,
|
||
tomorrow(),
|
||
})
|
||
|
||
end
|
||
|
||
local function playTime(time)
|
||
local h, m, s =
|
||
math.floor(time / 60 / 60),
|
||
math.floor(time / 60) % 60,
|
||
math.floor(time) % 60
|
||
return string.format('%02i:%02i:%02i', h, m, s)
|
||
end
|
||
|
||
function test.saveAttempt(ply, questions, answers, total)
|
||
local length = CurTime() - ply.dbg_playertest.start
|
||
local ptime = playTime(ply:GetTimeTotal())
|
||
|
||
local quiz = {}
|
||
for qID, q in ipairs(ply.dbg_playertest.quiz) do
|
||
local ans = {q.question}
|
||
for i = 1, #q.data do
|
||
ans[#ans + 1] = { q.data[i][1], answers[qID][i] and 1 or 0, q.data[i][2] and 1 or 0 }
|
||
end
|
||
quiz[#quiz + 1] = ans
|
||
end
|
||
local sid, playerinfo = ply:SteamID()
|
||
local req = octolib.vars.get('dbgTest.required')
|
||
octolib.func.chain({
|
||
function(nxt)
|
||
octolib.getSteamData({ util.SteamIDTo64(ply:SteamID()) }, nxt)
|
||
end,
|
||
function(nxt, result)
|
||
playerinfo = result[1]
|
||
storageApi:post('/post', {
|
||
user = {
|
||
name = playerinfo.name,
|
||
sid64 = playerinfo.steamid64,
|
||
},
|
||
total = total,
|
||
req = req,
|
||
quiz = quiz,
|
||
}):Then(nxt):Catch(ErrorNoHalt)
|
||
end,
|
||
function(nxt, response)
|
||
local embed = {
|
||
title = playerinfo.name,
|
||
url = 'https://steamcommunity.com/profiles/' .. playerinfo.steamid64,
|
||
description = 'Игрок ' .. (total >= req and 'прошел' or 'провалил') .. ' тест\nhttps://dbgtest.octo.gg/' .. response.data.key,
|
||
color = total >= req and 0x00ff00 or 0xff0000,
|
||
thumbnail = { url = playerinfo.avatar },
|
||
fields = {
|
||
{
|
||
name = 'SteamID',
|
||
value = sid,
|
||
}, {
|
||
name = 'Набрано баллов',
|
||
value = total..' / '..#questions..' ('..total..' / '..req..')',
|
||
}, {
|
||
name = 'Наигранное время',
|
||
value = ptime,
|
||
}, {
|
||
name = 'Время прохождения',
|
||
value = octolib.time.formatDuration(length),
|
||
},
|
||
},
|
||
footer = {
|
||
text = 'Ссылка с результатами действительна в течение 10 дней',
|
||
}
|
||
}
|
||
|
||
if CFG.webhooks.unban then
|
||
octoservices:post('/discord/webhook/' .. CFG.webhooks.unban, {
|
||
embeds = { embed },
|
||
})
|
||
end
|
||
|
||
end,
|
||
})
|
||
|
||
end
|
||
|
||
function test.reset(steamID)
|
||
|
||
octolib.setDBVar(steamID, dbvar, nil)
|
||
|
||
end
|
||
|
||
function test.generate(questions, maxInBatch)
|
||
questions = questions or test.questions
|
||
maxInBatch = maxInBatch or octolib.vars.get('dbgTest.maxInBatch')
|
||
|
||
local quiz = {}
|
||
local forUs, forThem = {}, {}
|
||
|
||
-- create quiz, shuffling questions and answers
|
||
local src = table.Copy(questions)
|
||
for catID, cat in pairs(src) do
|
||
octolib.array.shuffle(cat)
|
||
for i = 1, math.min(maxInBatch, #cat) do
|
||
octolib.array.shuffle(cat[i].data)
|
||
quiz[#quiz + 1] = cat[i]
|
||
end
|
||
end
|
||
octolib.array.shuffle(quiz)
|
||
|
||
for qID, q in ipairs(quiz) do
|
||
forThem[qID] = {q.question}
|
||
forUs[qID] = q
|
||
for i = 1, #q.data do
|
||
forThem[qID][i+1] = q.data[i][1] -- send them questions only
|
||
end
|
||
end
|
||
|
||
return forUs, forThem
|
||
|
||
end
|
||
|
||
function test.spawned(ply)
|
||
|
||
local ip = ply:IPAddress():gsub('%:.+', '')
|
||
local ips = ply:GetDBVar('ips') or {}
|
||
if ip ~= '172.18.0.1' and not table.HasValue(ips, ip) then
|
||
ips[#ips + 1] = ip
|
||
while #ips > 3 do table.remove(ips, 1) end
|
||
|
||
ply:SetDBVar('ips', ips)
|
||
end
|
||
|
||
if CFG.requireLauncher and not ply:GetNetVar('launcherActivated') then
|
||
return
|
||
else
|
||
ply:SetNetVar('launcherActivated', true)
|
||
end
|
||
|
||
if CFG.dev and not CFG.testEnabled then
|
||
netstream.Start(ply, 'dbg-test.welcomeScreen')
|
||
return
|
||
end -- no test for dev
|
||
|
||
ply:Freeze(true)
|
||
ply:SetNoDraw(true)
|
||
ply:SetNotSolid(true)
|
||
|
||
local data = ply:GetDBVar(dbvar) or {}
|
||
if data == true then
|
||
netstream.Start(ply, 'dbg-test.welcomeScreen')
|
||
else
|
||
if data[2] and os.time() > data[2] then
|
||
ply:SetDBVar(dbvar, { test.config.attempts, tomorrow() })
|
||
end
|
||
ply.dbg_playertest = {}
|
||
test.welcome(ply)
|
||
end
|
||
|
||
end
|
||
hook.Add('PlayerFinishedLoading', 'dbg-test', test.spawned)
|
||
|
||
function test.welcome(ply, showMsg)
|
||
|
||
local attempts = test.getAttempts(ply)
|
||
local msg = (attempts == 0 and L.msgTryTomorrow or attempts == test.config.attempts and L.msgWelcome or L.msgTryAgain):format(attempts)
|
||
netstream.Start(ply, 'dbg-test.welcomeScreen', attempts, msg, showMsg)
|
||
|
||
end
|
||
|
||
function test.start(ply)
|
||
|
||
if ply.dbg_playertest and CurTime() < (ply.dbg_playertest.nextAttempt or 0) then
|
||
ply:Notify('warning', 'Ты уже проходишь тест! Если он не появился, подожди немного')
|
||
return
|
||
end
|
||
|
||
local attemptsLeft = test.getAttempts(ply)
|
||
if attemptsLeft <= 0 then
|
||
netstream.Start(ply, 'dbg-test.welcomeScreen', 0, L.msgTryTomorrow, true)
|
||
return
|
||
end
|
||
test.takeAttempt(ply)
|
||
|
||
local forUs, forThem = test.generate()
|
||
netstream.Start(ply, 'dbg-test.start', forThem, octolib.vars.get('dbgTest.required'))
|
||
|
||
ply.dbg_playertest = ply.dbg_playertest or {}
|
||
ply.dbg_playertest.start = CurTime()
|
||
ply.dbg_playertest.nextAttempt = CurTime() + 20
|
||
ply.dbg_playertest.quiz = forUs
|
||
|
||
end
|
||
netstream.Hook('dbg-test.start', test.start)
|
||
|
||
function test.calcScore(answers, correct)
|
||
local total, scores = 0, {}
|
||
for qID, q in ipairs(correct) do
|
||
local right, should = 0, 0
|
||
for i = 1, #q.data do
|
||
if q.data[i][2] then
|
||
should = should + 1
|
||
if answers[qID][i] then
|
||
right = right + 1
|
||
end
|
||
elseif answers[qID][i] then
|
||
should = should + 1
|
||
end
|
||
end
|
||
scores[qID] = {q.question, right, should}
|
||
total = total + (right / should)
|
||
end
|
||
return total, scores
|
||
end
|
||
|
||
function test.answer(ply, answers)
|
||
|
||
if not ply.dbg_playertest or not ply.dbg_playertest.quiz then
|
||
return test.pass(ply)
|
||
end
|
||
|
||
if test.getAttempts(ply) < 0 then
|
||
return test.catchExploit(ply)
|
||
end
|
||
|
||
local total, scores = test.calcScore(answers, ply.dbg_playertest.quiz)
|
||
test.saveAttempt(ply, ply.dbg_playertest.quiz, answers, total)
|
||
if total >= octolib.vars.get('dbgTest.required') then
|
||
test.pass(ply, true, scores)
|
||
else
|
||
test.welcome(ply, true)
|
||
end
|
||
|
||
end
|
||
netstream.Hook('dbg-test.answer', test.answer)
|
||
|
||
function test.catchExploit(ply)
|
||
|
||
ply.dbg_playertest.expAtt = (ply.dbg_playertest.expAtt or 0) + 1
|
||
ply:Notify('warning', L.exploits_not_here2)
|
||
|
||
if ply.dbg_playertest.expAtt >= 3 then
|
||
ply:Kick(L.exploits_not_here)
|
||
end
|
||
|
||
end
|
||
|
||
function test.pass(ply, justPassed, data)
|
||
|
||
if ply.passedTest then
|
||
return ply:addExploitAttempt()
|
||
end
|
||
|
||
if justPassed then
|
||
ply:SetDBVar(dbvar, true)
|
||
end
|
||
|
||
ply.dbg_playertest = nil
|
||
ply.passedTest = true
|
||
netstream.Start(ply, 'dbg-test.answer', data)
|
||
|
||
ply:Spawn()
|
||
ply:Freeze(false)
|
||
ply:SetNoDraw(false)
|
||
ply:SetNotSolid(false)
|
||
ply:SetMoveType(MOVETYPE_WALK)
|
||
|
||
hook.Run('dbg-test.complete', ply, justPassed, data)
|
||
end
|
||
|
||
netstream.Hook('dbg-flyover.requestPos', function(ply, pos)
|
||
if ply.passedTest then return end
|
||
ply:SetPos(pos)
|
||
ply:Freeze(true)
|
||
ply:SetNoDraw(true)
|
||
ply:SetNotSolid(true)
|
||
ply:SetMoveType(MOVETYPE_NOCLIP)
|
||
-- timer.Create('resetPVS_' .. ply:SteamID(), 5, 1, function()
|
||
-- if not IsValid(ply) then return end
|
||
-- ply.extendPVS = nil
|
||
-- end)
|
||
end)
|
||
|
||
-- hook.Add('SetupPlayerVisibility', 'dbg-flyover', function(ply)
|
||
-- if not ply.extendPVS then return end
|
||
-- AddOriginToPVS(ply.extendPVS)
|
||
-- end)
|