if (!SH_REPORTS.ActiveReports) then SH_REPORTS.ActiveReports = {} SH_REPORTS.UniqueID = 0 SH_REPORTS.InsertSQL = "INSERT IGNORE INTO" end if (SH_REPORTS.UseWorkshop) then -- resource.AddWorkshop("1141886968") else resource.AddFile("materials/shenesis/general/back.png") resource.AddFile("materials/shenesis/general/close.png") resource.AddFile("materials/shenesis/reports/add.png") resource.AddFile("materials/shenesis/reports/stats.png") resource.AddFile("materials/shenesis/reports/star.png") resource.AddFile("resource/fonts/circular.ttf") resource.AddFile("resource/fonts/circular_bold.ttf") end function SH_REPORTS:DatabaseConnected() if (self.DatabaseMode == "mysqloo") then self:Query("SHOW TABLES LIKE 'sh_reports_performance'", function(q, ok, data) if (!ok) or (data and table.Count(data) > 0) then self:PostDatabaseConnected() return end self:Query([[ CREATE TABLE `sh_reports_performance` ( `steamid` varchar(64) COLLATE utf8_unicode_ci NOT NULL, `claimed` int(10) unsigned DEFAULT '0', `closed` int(10) unsigned DEFAULT '0', `timespent` int(10) unsigned DEFAULT '0', `report_id` int(10) unsigned DEFAULT '0', UNIQUE KEY `steamid_UNIQUE` (`steamid`,`report_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; CREATE TABLE `sh_reports_performance_reports` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `start_time` int(10) unsigned DEFAULT '0', `end_time` int(10) unsigned DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; ]], function(q2, ok2, data2) self:DBPrint("Creating sh_reports_performance and sh_reports_performance_reports: " .. tostring(ok2) .. " (" .. tostring(data2) /* 76561198398853149 */ .. ")") self:PostDatabaseConnected() end) end) self:Query("SHOW TABLES LIKE 'sh_reports_performance_ratings'", function(q, ok, data) if (!ok) or (data and table.Count(data) > 0) then return end self:Query([[ CREATE TABLE `sh_reports_performance_ratings` ( `steamid` varchar(64) COLLATE utf8_unicode_ci NOT NULL, `total` int(10) unsigned DEFAULT '0', `num` int(10) unsigned DEFAULT '0', PRIMARY KEY (`steamid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; ]], function(q2, ok2, data2) self:DBPrint("Creating sh_reports_performance_ratings: " .. tostring(ok2) .. " (" .. tostring(data2) /* 76561198398853124 */ .. ")") end) end) self:Query("SHOW TABLES LIKE 'sh_reports_performance_history'", function(q, ok, data) if (!ok) or (data and table.Count(data) > 0) then return end self:Query([[ CREATE TABLE `sh_reports_performance_history` ( `id` int(10) unsigned NOT NULL, `reporter` varchar(64) NOT NULL, `reported` varchar(64) NOT NULL, `reason` varchar(256), `comment` varchar(2048), `waiting_time` int(10) unsigned DEFAULT '0', `date` int(10) unsigned DEFAULT '0', `admin` varchar(64) NOT NULL, `rating` int(10) unsigned DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; ]], function(q2, ok2, data2) self:DBPrint("Creating sh_reports_performance_history: " .. tostring(ok2) .. " (" .. tostring(data2) .. ")") end) end) else local function CreateTable(name, query) if (!sql.TableExists(name)) then sql.Query([[ CREATE TABLE `]] .. name .. [[` (]] .. query .. [[) ]]) self:DBPrint("Creating " .. name .. ": " .. tostring(sql.TableExists(name))) end end CreateTable("sh_reports_performance", [[ `steamid` varchar(64) NOT NULL, `claimed` int(10) DEFAULT '0', `closed` int(10) DEFAULT '0', `timespent` int(10) DEFAULT '0', `report_id` int(10) DEFAULT '0', UNIQUE(steamid, report_id) ON CONFLICT IGNORE ]]) CreateTable("sh_reports_performance_reports", [[ `id` int(10) NOT NULL PRIMARY KEY, `start_time` int(10) DEFAULT '0', `end_time` int(10) DEFAULT '0' ]]) CreateTable("sh_reports_performance_ratings", [[ `steamid` varchar(64) NOT NULL PRIMARY KEY, `total` int(10) DEFAULT '0', `num` int(10) DEFAULT '0' ]]) CreateTable("sh_reports_performance_history", [[ `id` int(10) NOT NULL PRIMARY KEY, `reporter` varchar(64) NOT NULL, `reported` varchar(64) NOT NULL, `reason` varchar(256), `comment` varchar(2048), `waiting_time` int(10) DEFAULT '0', `date` int(10) DEFAULT '0', `admin` varchar(64) NOT NULL, `rating` int(5) DEFAULT '0' ]]) self.InsertSQL = "INSERT OR IGNORE INTO" self:PostDatabaseConnected() end end function SH_REPORTS:PostDatabaseConnected() self:BetterQuery("SELECT * FROM sh_reports_performance_reports WHERE {time} < end_time ORDER BY id DESC", {time = os.time()}, function(q, ok, data) if (!ok) then return end if (data and #data > 0) then local d = table.Copy(data[1]) d.id = tonumber(d.id) d.start_time = tonumber(d.start_time) d.end_time = tonumber(d.end_time) self.CurrentPerfReport = d self:DBPrint("Using performance report #" .. d.id .. ". It will last until " .. os.date(self.DateFormat, d.end_time) .. " 00:00.") else self:DBPrint("Creating new performance report as none were found.") self:CreatePerformanceReport() end end) if (self.StorageExpiryTime > 0) then self:BetterQuery("DELETE FROM sh_reports_performance_history WHERE {time} > date", {time = os.time() - self.StorageExpiryTime}) end end function SH_REPORTS:CreatePerformanceReport() local days = 1 if (self.PerformanceFrequency == "weekly") then days = 7 - tonumber(os.date("%w")) + self.PerformanceWeekDay elseif (self.PerformanceFrequency == "monthly") then days = 31 end local mthen = self:GetMidnight(days) self:Query("SELECT id FROM sh_reports_performance_reports", function(q, ok, data) if (!ok) then return end local d = {id = table.Count(data) + 1, start_time = os.time(), end_time = mthen} self.CurrentPerfReport = d self:BetterQuery([[ INSERT INTO sh_reports_performance_reports (id, start_time, end_time) VALUES ({id}, {start_time}, {end_time}) ]], d) self:DBPrint("Created performance report #" .. d.id .. ". It will last until " .. os.date(self.DateFormat, mthen) .. " 00:00.") end) self.CachedPerfReports = nil end function SH_REPORTS:NewReport(ply, data) if (self:IsAdmin(ply) and !self.StaffCanReport) then self:Notify(ply, "cannot_report_as_admin", false) return end if (data.reporter_id == data.reported_id) then self:Notify(ply, "cannot_report_self", false) return end local target = player.GetBySteamID64(data.reported_id) if (IsValid(target) and self:IsAdmin(target) and !self.StaffCanBeReported) then self:Notify(ply, "cannot_report_admin", false) return end local sid = ply:SteamID64() if (table.Count(self:GetAllReports(sid)) >= self.MaxReportsPerPlayer) then self:Notify(ply, "report_limit_reached", false) return end if (data.reported_id == "0" and !self.CanReportOther) then return end self.UniqueID = self.UniqueID + 1 data.id = self.UniqueID self.ActiveReports[data.id] = table.Copy(data) self:Notify(ply, "report_submitted", true) self:Log(ply:Nick() .. " <" .. ply:SteamID() .. "> reported [#" .. data.id .. "] " .. data.reported_name .. " <" .. util.SteamIDFrom64(data.reported_id /* 76561198398853124 */) .. "> for " .. self.ReportReasons[data.reason_id]) easynet.Send(self:GetStaff(), "SH_REPORTS.ReportCreated", data) end function SH_REPORTS:PlayerSay(ply, str) local text = str:Replace("!", "/"):lower():Trim() if (self.AdminCommands[text]) then self:ShowReports(ply) return "" end if (self.ReportCommands[text]) then if (!self:IsAdmin(ply) or self.StaffCanReport) then easynet.Send(ply, "SH_REPORTS.QuickReport", {comment = "", lastkiller = ply.SH_LastKiller, lastarrester = ply.SH_LastArrester}) else self:Notify(ply, "cannot_report_as_admin", false) end return "" end if (self.EnableQuickReport and !self:IsAdmin(ply) and text:StartWith("@")) then easynet.Send(ply, "SH_REPORTS.QuickReport", {comment = str:sub(2), lastkiller = ply.SH_LastKiller, lastarrester = ply.SH_LastArrester}) return "" end if (text == "/reportstats") then if (self:IsAdmin(ply)) then self:BetterQuery("SELECT * FROM sh_reports_performance WHERE steamid = {steamid}", {steamid = ply:SteamID64()}, function(q, ok, data) if (!ok or !IsValid(ply)) then return end local claimed = 0 local closed = 0 for _, d in pairs (data) do claimed = claimed + tonumber(d.claimed) closed = closed + tonumber(d.closed) end ply:ChatPrint("Reports claimed: " .. string.Comma(claimed) .. " | Reports closed: " .. string.Comma(closed)) end) end return "" end end function SH_REPORTS:ShowReports(ply) local tosend = {} if (self:IsAdmin(ply)) then -- If admin, send all reports, if not only send own tosend = self:GetAllReports() else tosend = self:GetAllReports(ply:SteamID64()) end easynet.Send(ply, "SH_REPORTS.SendList", { server_time = os.time(), struct_reports = tosend, }) end local function SendPerfReports(ply, preps) easynet.Send(ply, "SH_REPORTS.SendPerfReports", { struct_perf_reports = preps }) end function SH_REPORTS:ShowPerformanceReports(ply) if (!self.UsergroupsPerformance[ply:GetUserGroup()]) then self:Notify(ply, "not_allowed_to_run_cmd", false) return end if (self.CachedPerfReports) then SendPerfReports(ply, self.CachedPerfReports) else self:BetterQuery("SELECT * FROM sh_reports_performance_reports", {time = os.time()}, function(q, ok, data) if (!ok or !IsValid(ply)) then return end local d = {} for k, v in pairs (data) do d[tonumber(v.id)] = v end self.CachedPerfReports = d if (IsValid(ply)) then SendPerfReports(ply, d) end end) end end function SH_REPORTS:RequestPerfReportStaff(ply, id) if (!self.UsergroupsPerformance[ply:GetUserGroup()]) then self:Notify(ply, "not_allowed_to_run_cmd", false) return end self:BetterQuery("SELECT steamid, claimed, closed, timespent FROM sh_reports_performance WHERE report_id = {id}" /* 76561198398853149 */, {id = id}, function(q, ok, data) if (!ok or !IsValid(ply)) then return end for k, v in pairs (data) do v.claimed = tonumber(v.claimed) or 0 v.closed = tonumber(v.closed) or 0 v.timespent = tonumber(v.timespent) or 0 end easynet.Send(ply, "SH_REPORTS.SendPerfReportStaff", { id = id, struct_perf_reports_staff = data }) end) end function SH_REPORTS:RequestStaffRatings(ply) if (!self.UsergroupsPerformance[ply:GetUserGroup()]) then self:Notify(ply, "not_allowed_to_run_cmd", false) return end self:BetterQuery("SELECT steamid, num, total FROM sh_reports_performance_ratings" /* 76561198398853124 */, {}, function(q, ok, data) if (!ok or !IsValid(ply)) then return end easynet.Send(ply, "SH_REPORTS.SendRatings", { struct_rating = data }) end) end function SH_REPORTS:RequestReportHistory(ply) if (!self.UsergroupsPerformance[ply:GetUserGroup()]) then self:Notify(ply, "not_allowed_to_run_cmd", false) return end self:BetterQuery("SELECT * FROM sh_reports_performance_history", {}, function(q, ok, data) if (!ok or !IsValid(ply)) then return end -- Factorize the SteamID's to save on bytes local t_steamids = {} local t = {} for _, dat in pairs (data) do t[tonumber(dat.id)] = dat t_steamids[dat.reporter] = true t_steamids[dat.reported] = true t_steamids[dat.admin] = true end local steamids = {} for steamid in pairs (t_steamids) do t_steamids[steamid] = table.insert(steamids, {steamid = steamid}) end local t_list = {} for id, dat in pairs (t) do table.insert(t_list, { report_id = tonumber(dat.id), reporter_nid = t_steamids[dat.reporter], reported_nid = t_steamids[dat.reported], reason = dat.reason, comment = dat.comment, rating = dat.rating, date = dat.date, waiting_time = dat.waiting_time, admin_nid = t_steamids[dat.admin], }) end easynet.Send(ply, "SH_REPORTS.SendHistoryList", { struct_history_steamids = steamids, struct_history_list = t_list, }) end) end function SH_REPORTS:ClaimReport(admin, report) local sid = admin:SteamID64() for _, rep in pairs (self:GetAllReports()) do if (rep.admin_id == sid) then self:Notify(admin, "claimed_report_still_active", false) return false end end if (report.admin_id ~= "") then return false end report.claim_time = os.time() report.admin_id = sid self:Notify(player.GetBySteamID64(report.reporter_id), "admin_claimed_your_report", true) easynet.Send(admin, "SH_REPORTS.MinimizeReport", {report_id = report.id}) easynet.Send(self:GetStaff(), "SH_REPORTS.ReportClaimed", {report_id = report.id, admin_id = report.admin_id}) if (self.CurrentPerfReport) and (!report.is_admin or self.AdminReportsCount) then self:BetterQuery([[ ]] .. self.InsertSQL .. [[ sh_reports_performance (steamid, report_id) VALUES ({steamid}, {report_id}); UPDATE sh_reports_performance SET claimed = claimed + 1 WHERE steamid = {steamid} AND report_id = {report_id} ]], {steamid = admin:SteamID64(), report_id = self.CurrentPerfReport.id}) end return true end function SH_REPORTS:ClaimAndTeleport(admin, id, bring, bring_reported) if (!self:IsAdmin(admin)) then self:Notify(admin, "not_allowed_to_run_cmd", false) return end local report = self:FindReport(id) if (!report) then self:Notify(admin, "report_non_existent", false) return end if (self.ClaimNoTeleport) then return end local target = player.GetBySteamID64(report.reporter_id) if (!IsValid(target)) then return end if (!self:ClaimReport(admin, report)) then return end admin.SH_PosBeforeReport = admin:GetPos() target.SH_PosBeforeReport = target:GetPos() if (self.UseULXCommands) then -- Bad idea? ULX sucks anyways if (bring) then ulx.bring(admin, {target}) else ulx.goto(admin, target) end if (bring_reported) then local reported = player.GetBySteamID64(report.reported_id) if (IsValid(reported)) then reported.SH_PosBeforeReport = reported:GetPos() if (bring) then ulx.bring(admin, {reported}) else ulx.send(admin, reported, target) end end end else local a, b = admin, target if (bring) then a, b = target, admin end self:TeleportPlayer(a, b:GetPos()) if (bring_reported) then local reported = player.GetBySteamID64(report.reported_id) if (IsValid(reported)) then reported.SH_PosBeforeReport = reported:GetPos() self:TeleportPlayer(reported, b:GetPos()) end end end self:Log(admin:Nick() .. " <" .. admin:SteamID() .. "> claimed " .. target:Nick() .. "'s <" .. target:SteamID() .. "> report [#" .. id .. "]") end function SH_REPORTS:ClaimAndCSit(admin, id) if (!csitsystem) then return end if (!self:IsAdmin(admin)) then self:Notify(admin, "not_allowed_to_run_cmd", false) return end local report = self:FindReport(id) if (!report) then self:Notify(admin, "report_non_existent", false) return end local target = player.GetBySteamID64(report.reporter_id) if (!IsValid(target)) then return end if (!self:ClaimReport(admin, report)) then return end admin.SH_PosBeforeReport = admin:GetPos() target.SH_PosBeforeReport = target:GetPos() report.sit_id = csitsystem.HandOver(admin, target, player.GetBySteamID64(report.reported_id)) self:Log(admin:Nick() .. " <" .. admin:SteamID() .. "> claimed " .. target:Nick() .. "'s <" .. target:SteamID() .. "> report [#" .. id .. "] and started a sit") -- self:CloseReport(admin, id) end function SH_REPORTS:CloseReport(ply, id) local report = self:FindReport(id) if (!report) then self:Notify(ply, "report_non_existent", false) return end local sid = ply:SteamID64() if (self:IsAdmin(ply) and ((report.admin_id == "" and self.CanDeleteWhenUnclaimed) or report.admin_id == sid)) or (report.reporter_id == sid) then self.ActiveReports[id] = nil self:Notify(ply, "report_closed", true) easynet.Send(self:GetStaff(), "SH_REPORTS.ReportClosed", {report_id = id}) local target = player.GetBySteamID64(report.reporter_id) local admin = player.GetBySteamID64(report.admin_id) if (IsValid(target)) then if (report.reporter_id ~= sid) then self:Notify(target, "your_report_was_closed", true) elseif (report.admin_id ~= "") then if (IsValid(admin) and admin ~= ply) then self:Notify(admin, "reporter_closed_report", true) end end easynet.Send(target, "SH_REPORTS.ReportClosed", {report_id = id}) end if (!report.is_admin or self.AdminReportsCount) then if (report.admin_id ~= "") then local claim_time = os.time() - report.claim_time if (self.CurrentPerfReport) then self:BetterQuery([[ ]] .. self.InsertSQL .. [[ sh_reports_performance (steamid, report_id, timespent) VALUES ({steamid}, {report_id}, {timespent}); UPDATE sh_reports_performance SET closed = closed + 1, timespent = timespent + {timespent} WHERE steamid = {steamid} AND report_id = {report_id} ]], {steamid = report.admin_id, report_id = self.CurrentPerfReport.id, timespent = claim_time}) end if (sid == report.reporter_id) then self:Log(ply:Nick() .. " <" .. ply:SteamID() .. "> closed their own report [#" .. id .. "]") else if (IsValid(target) and self.AskRating) then if (!target.SH_ReportsCompleted) then target.SH_ReportsCompleted = {} end target.SH_ReportsCompleted[id] = ply:SteamID64() easynet.Send(target, "SH_REPORTS.PromptRating", {report_id = id, admin_name = ply:Nick()}) end self:Log(ply:Nick() .. " <" .. ply:SteamID() .. "> closed the report [#" .. id .. "] from " .. report.reporter_name .. "<" .. util.SteamIDFrom64(report.reporter_id) .. ">") end elseif (self:IsAdmin(ply)) then if (self.CurrentPerfReport) then self:BetterQuery([[ ]] .. self.InsertSQL .. [[ sh_reports_performance (steamid, report_id) VALUES ({steamid}, {report_id}); UPDATE sh_reports_performance SET closed = closed + 1 WHERE steamid = {steamid} AND report_id = {report_id} ]], {steamid = sid, report_id = self.CurrentPerfReport.id}) end if (sid == report.reporter_id) then self:Log(ply:Nick() .. " <" .. ply:SteamID() .. "> closed their own UNCLAIMED report [#" .. id .. "]") else self:Log(ply:Nick() .. " <" .. ply:SteamID() .. "> closed the UNCLAIMED report [#" .. id .. "] from " .. report.reporter_name .. "<" .. util.SteamIDFrom64(report.reporter_id) .. ">") end end end if (report.admin_id ~= "") then if (report.sit_id) then csitsystem.EndSit(report.sit_id) end if (self.TeleportPlayersBack) then if (IsValid(target)) then self:ReturnPlayer(target) end local admin = player.GetBySteamID64(report.admin_id) if (IsValid(admin)) then self:ReturnPlayer(admin) end local reported = player.GetBySteamID64(report.reported_id) if (IsValid(reported)) then self:ReturnPlayer(reported) end end if (self.StoreCompletedReports ~= "none") then self:BetterQuery([[ ]] .. self.InsertSQL .. [[ sh_reports_performance_history (id, reporter, reported, reason, comment, waiting_time, date, admin) VALUES ({id}, {reporter}, {reported}, {reason}, {comment}, {waiting_time}, {date}, {admin}); ]], {id = id, reporter = report.reporter_id, reported = report.reported_id, reason = self.ReportReasons[report.reason_id], comment = report.comment, waiting_time = os.time() - report.time, date = os.time(), admin = report.admin_id}) end end else self:Notify(ply, "not_allowed_to_run_cmd", false) end end function SH_REPORTS:RateAdmin(ply, report_id, rating) if (!ply.SH_ReportsCompleted or !ply.SH_ReportsCompleted[report_id]) then self:Notify(ply, "report_non_existent", false) return end local admin_id = ply.SH_ReportsCompleted[report_id] rating = math.Clamp(rating, 1, 5) self:BetterQuery([[ ]] .. self.InsertSQL .. [[ sh_reports_performance_ratings (steamid, total, num) VALUES ({steamid}, 0, 0); UPDATE sh_reports_performance_ratings SET total = total + {rating}, num = num + 1 WHERE steamid = {steamid} ]], {steamid = admin_id, rating = rating}) if (self.StoreCompletedReports ~= "none") then self:BetterQuery([[ UPDATE sh_reports_performance_history SET rating = {rating} WHERE id = {id} ]], {id = report_id, rating = rating}) end if (self.NotifyRating) then local admin = player.GetBySteamID64(admin_id) if (IsValid(admin)) then local rstr = "" for i = 1, 5 do rstr = rstr .. (rating >= i and "★" or "☆") end self:Notify(admin, "rate_notification\t" .. ply:Nick() .. "\t" .. rstr, rating >= 3) end end ply.SH_ReportsCompleted[report_id] = nil self:Notify(ply, "rate_thanks", true) end function SH_REPORTS:PlayerReady(ply) if (self.NotifyAdminsOnConnect and self:IsAdmin(ply)) then local num = 0 local pending = {} for id, report in pairs (self:GetAllReports()) do if (report.admin_id == "") then num = num + 1 table.insert(pending, report) end end if (num > 0) then easynet.Send(ply, "SH_REPORTS.ReportsPending", {num = num, struct_reports = pending}) end end end function SH_REPORTS:PlayerDisconnected(ply) local sid = ply:SteamID64() for id, report in pairs (self:GetAllReports()) do if (report.reporter_id == sid) then self.ActiveReports[id] = nil easynet.Send(self:GetStaff(), "SH_REPORTS.ReportClosed", {report_id = id}) local admin = player.GetBySteamID64(report.admin_id) if (IsValid(admin)) then self:Notify(admin, "reporter_closed_report", false) end elseif (self:IsAdmin(ply) and report.admin_id == sid) then report.admin_id = "" easynet.Send(self:GetStaff(), "SH_REPORTS.AdminLeft", {report_id = id}) local reporter = player.GetBySteamID64(report.reporter_id) if (IsValid(reporter)) then easynet.Send(reporter, "SH_REPORTS.AdminLeft", {report_id = id}) self:Notify(reporter, "admin_has_disconnected", false) end end end end function SH_REPORTS:ReturnPlayer(ply) if (!ply.SH_PosBeforeReport) then return end ply:SetPos(ply.SH_PosBeforeReport) ply.SH_PosBeforeReport = nil end function SH_REPORTS:MidnightCheck() local perf = self.CurrentPerfReport if (!perf) then return end if (os.time() >= perf.end_time) then self:DBPrint("Current performance report #" .. perf.id .. " expired, creating new one..") self:CreatePerformanceReport() end end function SH_REPORTS:Notify(ply, msg, good) easynet.Send(ply, "SH_REPORTS.Notify", {msg = msg, positive = good}) end function SH_REPORTS:GetStaff(ply) local t = {} for _, v in ipairs (player.GetAll()) do if (self:IsAdmin(v)) then table.insert(t, v) end end return t end local function CheckObstruction(ply, pos) local t = { start = pos, endpos = pos + Vector(0, 0, 72), mins = Vector(-16, -16, 0), maxs = Vector(16, 16, 4), filter = ply } return bit.band(util.PointContents(pos), CONTENTS_SOLID) > 0 or util.TraceHull(t).Hit end local coords = { Vector(48, 0, 0), Vector(-48, 0, 0), Vector(0, 48, 0), Vector(0, -48, 0), Vector(48, 48, 0), Vector(-48, 48, 0), Vector(48, -48, 0), Vector(-48, -48, 0), } function SH_REPORTS:TeleportPlayer(ply, pos, exact) if (!exact) then if (CheckObstruction(ply, pos)) then for _, c in ipairs (coords) do if (!util.TraceLine({start = pos, endpos = pos + c, filter = ents.GetAll()}).Hit and !CheckObstruction(ply, pos + c /* 76561198398853124 */)) then pos = pos + c break end end end end ply.SH_PositionBeforeTeleport = ply:GetPos() ply:SetPos(pos) end function SH_REPORTS:FindReport(id) return self.ActiveReports[id] end function SH_REPORTS:GetAllReports(author) local t = {} for id, report in pairs (self.ActiveReports) do if (author and report.reporter_id ~= author) then continue end t[id] = report end return t end function SH_REPORTS:Log(s) if (!self.UseServerLog) then return end ServerLog(s .. "\n") end local function GetPerformanceReport(date, preps) for id, ps in pairs (preps) do if (date >= ps[1] and date <= ps[2]) then return id end end end function SH_REPORTS:RebuildPerformance() local preps = {} self:BetterQuery("SELECT * FROM sh_reports_performance_reports", {}, function(q, ok, data) for k, v in pairs (data) do preps[tonumber(v.id)] = {tonumber(v.start_time), tonumber(v.end_time)} end self:BetterQuery("SELECT * FROM sh_reports_performance_history", {}, function(q, ok, data) local staffreps = {} for _, rep in SortedPairsByMemberValue (data, "date") do local admin = tostring(rep.admin) staffreps[admin] = staffreps[admin] or {} table.insert(staffreps[admin], rep) end local staffpreps = {} for sid, reps in pairs (staffreps) do staffpreps[sid] = {} for _, rep in pairs (reps) do local prepid = GetPerformanceReport(tonumber(rep.date), preps) staffpreps[sid][prepid] = staffpreps[sid][prepid] or {} table.insert(staffpreps[sid][prepid], rep) end end for sid, preps in pairs (staffpreps) do for prepid, reps in pairs (preps) do local claimed, closed, timespent = 0, 0, 0 for _, rep in pairs (reps) do claimed = claimed + 1 closed = closed + 1 timespent = timespent + tonumber(rep.waiting_time) end self:BetterQuery("SELECT * FROM sh_reports_performance WHERE steamid = {steamid} AND report_id = {prepid}", {steamid = sid, prepid = prepid}, function(q, ok, data) if (data and data[1]) then self:BetterQuery("UPDATE sh_reports_performance SET claimed = {claimed}, closed = {closed}, timespent = {timespent} WHERE steamid = {steamid} AND report_id = {prepid}", {claimed = claimed, closed = closed, timespent = timespent, steamid = sid, prepid = prepid}) else self:BetterQuery("INSERT INTO sh_reports_performance (steamid, claimed, closed, timespent, report_id) VALUES ({steamid}, {claimed}, {closed}, {timespent}, {prepid})", {claimed = claimed, closed = closed, timespent = timespent, steamid = sid, prepid = prepid}) end end) end end end) end) end hook.Add("PlayerDisconnected", "SH_REPORTS.PlayerDisconnected", function(ply) SH_REPORTS:PlayerDisconnected(ply) end) hook.Add("PlayerSay", "SH_REPORTS.PlayerSay", function(ply, str) local r = SH_REPORTS:PlayerSay(ply, str) if (r) then return r end end) hook.Add("DoPlayerDeath", "SH_REPORTS.DoPlayerDeath", function(ply, atk, dmginfo) if (IsValid(atk) and atk:IsPlayer() and atk ~= ply) then ply.SH_LastKiller = atk end end) hook.Add("playerArrested", "SH_REPORTS.playerArrested", function(ply, time, arrester) if (IsValid(arrester) and arrester:IsPlayer() and arrester ~= ply) then ply.SH_LastArrester = arrester end end) hook.Add("PlayerButtonDown", "SH_REPORTS.PlayerButtonDown", function(ply, btn) if (!IsFirstTimePredicted()) then return end if (btn == SH_REPORTS.ReportKey) then if (!SH_REPORTS:IsAdmin(ply) or SH_REPORTS.StaffCanReport) then easynet.Send(ply, "SH_REPORTS.QuickReport", {comment = "", lastkiller = ply.SH_LastKiller, lastarrester = ply.SH_LastArrester}) else SH_REPORTS:Notify(ply, "cannot_report_as_admin", false) end elseif (btn == SH_REPORTS.ReportsKey) then SH_REPORTS:ShowReports(ply) end end) timer.Create("SH_REPORTS.MidnightCheck", 1, 0, function() SH_REPORTS:MidnightCheck() end) easynet.Callback("SH_REPORTS.NewReport", function(data, ply) local report = data data.reporter_name = ply:Nick() data.reporter_id = ply:SteamID64() data.time = os.time() data.admin_id = "" data.comment = data.comment:sub(1, SH_REPORTS.MaxCommentLength) data.is_admin = SH_REPORTS:IsAdmin(ply) SH_REPORTS:NewReport(ply, data) end) easynet.Callback("SH_REPORTS.Claim", function(data, ply) if (!SH_REPORTS:IsAdmin(ply)) then SH_REPORTS:Notify(ply, "not_allowed_to_run_cmd", false) return end local report = SH_REPORTS:FindReport(data.id) if (!report) then SH_REPORTS:Notify(ply, "report_non_existent", false) return end SH_REPORTS:ClaimReport(ply, report) end) easynet.Callback("SH_REPORTS.ClaimAndTeleport", function(data, ply) SH_REPORTS:ClaimAndTeleport(ply, data.id, data.bring, data.bring_reported) end) easynet.Callback("SH_REPORTS.ClaimAndCSit", function(data, ply) SH_REPORTS:ClaimAndCSit(ply, data.id) end) easynet.Callback("SH_REPORTS.CloseReport", function(data, ply) SH_REPORTS:CloseReport(ply, data.id) end) easynet.Callback("SH_REPORTS.RequestPerfReports", function(data, ply) SH_REPORTS:ShowPerformanceReports(ply) end) easynet.Callback("SH_REPORTS.RequestPerfReportStaff", function(data, ply) SH_REPORTS:RequestPerfReportStaff(ply, data.id) end) easynet.Callback("SH_REPORTS.PlayerReady", function(data, ply) SH_REPORTS:PlayerReady(ply) end) easynet.Callback("SH_REPORTS.RateAdmin", function(data, ply) SH_REPORTS:RateAdmin(ply, data.report_id, data.rating) end) easynet.Callback("SH_REPORTS.RequestStaffRatings", function(data, ply) SH_REPORTS:RequestStaffRatings(ply) end) easynet.Callback("SH_REPORTS.RequestReportHistory", function(data, ply) SH_REPORTS:RequestReportHistory(ply) end) -- vk.com/urbanichka