456 lines
15 KiB
Lua
456 lines
15 KiB
Lua
local PLUGIN = PLUGIN
|
||
|
||
util.AddNetworkString("ixServerLogs_Request")
|
||
util.AddNetworkString("ixServerLogs_Response")
|
||
util.AddNetworkString("ixServerLogs_Clear")
|
||
util.AddNetworkString("ixServerLogs_OpenMenu")
|
||
|
||
-- Функция проверки доступа к логам через CAMI
|
||
local function HasLogAccess(client)
|
||
return AdminPrivs[client:GetUserGroup()]
|
||
end
|
||
|
||
-- Инициализация системы логов
|
||
function PLUGIN:InitializeLogSystem()
|
||
self.logs = self.logs or {}
|
||
self.logFile = nil
|
||
|
||
-- Создаем папку для логов
|
||
if ix.config.Get("logSaveToFile", true) then
|
||
local logPath = ix.config.Get("logFilePath", "helix_logs")
|
||
if not file.Exists(logPath, "DATA") then
|
||
file.CreateDir(logPath)
|
||
end
|
||
|
||
-- Создаем файл для текущей сессии
|
||
local date = os.date("%Y-%m-%d_%H-%M-%S")
|
||
self.logFile = logPath .. "/log_" .. date .. ".txt"
|
||
|
||
file.Write(self.logFile, "=== Server Logs Started: " .. os.date("%Y-%m-%d %H:%M:%S") .. " ===\n")
|
||
end
|
||
end
|
||
|
||
-- Добавление лога
|
||
function PLUGIN:AddLog(logType, message, target, data)
|
||
if not self.LOG_TYPES[logType] then
|
||
print("[ServerLogs] Unknown log type: " .. tostring(logType))
|
||
return
|
||
end
|
||
|
||
local logEntry = {
|
||
id = #self.logs + 1,
|
||
type = logType,
|
||
category = self.LOG_TYPES[logType].category,
|
||
message = message,
|
||
target = target and {
|
||
name = IsValid(target) and target:Nick() or tostring(target),
|
||
steamid = IsValid(target) and target:SteamID() or "N/A"
|
||
} or nil,
|
||
data = data or {},
|
||
timestamp = os.time(),
|
||
timeString = os.date("%H:%M:%S")
|
||
}
|
||
|
||
table.insert(self.logs, logEntry)
|
||
|
||
-- Ограничиваем количество логов в памяти
|
||
local maxEntries = ix.config.Get("logMaxEntries", 1000)
|
||
if #self.logs > maxEntries then
|
||
table.remove(self.logs, 1)
|
||
end
|
||
|
||
-- Сохраняем в файл
|
||
if self.logFile then
|
||
local logString = string.format("[%s][%s] %s\n",
|
||
logEntry.timeString,
|
||
self.LOG_CATEGORIES[logEntry.category].name,
|
||
message
|
||
)
|
||
file.Append(self.logFile, logString)
|
||
end
|
||
|
||
-- Выводим в консоль
|
||
MsgC(self.LOG_CATEGORIES[logEntry.category].color, "[LOG] ", Color(255, 255, 255), message .. "\n")
|
||
end
|
||
|
||
-- Получение логов с фильтрацией
|
||
function PLUGIN:GetLogs(filters)
|
||
filters = filters or {}
|
||
local result = {}
|
||
|
||
for i = #self.logs, 1, -1 do
|
||
local log = self.logs[i]
|
||
local match = true
|
||
|
||
-- Фильтр по категории
|
||
if filters.category and log.category ~= filters.category then
|
||
match = false
|
||
end
|
||
|
||
-- Фильтр по типу
|
||
if filters.type and log.type ~= filters.type then
|
||
match = false
|
||
end
|
||
|
||
-- Фильтр по игроку
|
||
if filters.player and log.target and log.target.steamid ~= filters.player then
|
||
match = false
|
||
end
|
||
|
||
-- Фильтр по времени
|
||
if filters.startTime and log.timestamp < filters.startTime then
|
||
match = false
|
||
end
|
||
|
||
if filters.endTime and log.timestamp > filters.endTime then
|
||
match = false
|
||
end
|
||
|
||
-- Фильтр по тексту
|
||
if filters.search and not string.find(string.lower(log.message), string.lower(filters.search)) then
|
||
match = false
|
||
end
|
||
|
||
if match then
|
||
table.insert(result, log)
|
||
|
||
-- Ограничиваем количество результатов
|
||
if filters.limit and #result >= filters.limit then
|
||
break
|
||
end
|
||
end
|
||
end
|
||
|
||
return result
|
||
end
|
||
|
||
-- Сетевая обработка запроса логов
|
||
net.Receive("ixServerLogs_Request", function(len, client)
|
||
if not HasLogAccess(client) then return end
|
||
|
||
local filters = net.ReadTable()
|
||
local logs = PLUGIN:GetLogs(filters)
|
||
|
||
-- Отправляем логи порциями
|
||
local chunkSize = 50
|
||
for i = 1, #logs, chunkSize do
|
||
local chunk = {}
|
||
for j = i, math.min(i + chunkSize - 1, #logs) do
|
||
table.insert(chunk, logs[j])
|
||
end
|
||
|
||
net.Start("ixServerLogs_Response")
|
||
net.WriteUInt(i == 1 and 1 or 0, 2) -- Первый пакет или нет
|
||
net.WriteTable(chunk)
|
||
net.Send(client)
|
||
end
|
||
|
||
-- Отправляем финальный пакет если логов нет
|
||
if #logs == 0 then
|
||
net.Start("ixServerLogs_Response")
|
||
net.WriteUInt(1, 2)
|
||
net.WriteTable({})
|
||
net.Send(client)
|
||
end
|
||
end)
|
||
|
||
-- Очистка логов
|
||
net.Receive("ixServerLogs_Clear", function(len, client)
|
||
if not client:IsSuperAdmin() then return end
|
||
|
||
PLUGIN.logs = {}
|
||
PLUGIN:AddLog("ADMIN_COMMAND", client:Nick() .. " очистил логи сервера", client)
|
||
end)
|
||
|
||
-- ======================
|
||
-- ХУКИ ДЛЯ ЛОГИРОВАНИЯ
|
||
-- ======================
|
||
|
||
-- Смерть игрока
|
||
function PLUGIN:PlayerDeath(victim, inflictor, attacker)
|
||
local attackerName = "Мир"
|
||
local attackerSteamID = "world"
|
||
local weapon = "unknown"
|
||
|
||
if IsValid(attacker) and attacker:IsPlayer() then
|
||
attackerName = attacker:Nick()
|
||
attackerSteamID = attacker:SteamID()
|
||
weapon = IsValid(attacker:GetActiveWeapon()) and attacker:GetActiveWeapon():GetClass() or "unknown"
|
||
end
|
||
|
||
local message = ""
|
||
|
||
if IsValid(attacker) and attacker:IsPlayer() then
|
||
if attacker == victim then
|
||
message = victim:Nick() .. " покончил с собой"
|
||
else
|
||
message = attackerName .. " убил " .. victim:Nick() .. " используя " .. weapon
|
||
end
|
||
else
|
||
message = victim:Nick() .. " умер"
|
||
end
|
||
|
||
self:AddLog("PLAYER_DEATH", message, victim, {
|
||
attacker = attackerSteamID,
|
||
weapon = weapon
|
||
})
|
||
end
|
||
|
||
-- Получение урона
|
||
function PLUGIN:PlayerHurt(victim, attacker, health, damage)
|
||
if not IsValid(attacker) or not attacker:IsPlayer() or attacker == victim then return end
|
||
|
||
local message = string.format("%s нанёс %d урона игроку %s (осталось %d HP)",
|
||
attacker:Nick(), damage, victim:Nick(), health)
|
||
|
||
self:AddLog("PLAYER_HURT", message, victim, {
|
||
attacker = attacker:SteamID(),
|
||
damage = damage,
|
||
health = health
|
||
})
|
||
end
|
||
|
||
-- Подключение игрока
|
||
function PLUGIN:PlayerInitialSpawn(client)
|
||
local message = string.format("%s подключился к серверу (SteamID: %s)",
|
||
client:Nick(), client:SteamID())
|
||
|
||
self:AddLog("PLAYER_CONNECT", message, client)
|
||
end
|
||
|
||
-- Отключение игрока
|
||
function PLUGIN:PlayerDisconnected(client)
|
||
local message = string.format("%s отключился от сервера", client:Nick())
|
||
|
||
self:AddLog("PLAYER_DISCONNECT", message, client)
|
||
end
|
||
|
||
-- Спавн игрока
|
||
function PLUGIN:PlayerSpawn(client)
|
||
if not client:GetCharacter() then return end
|
||
|
||
local message = string.format("%s заспавнился", client:Nick())
|
||
|
||
self:AddLog("PLAYER_SPAWN", message, client)
|
||
end
|
||
|
||
-- Сообщения в чат
|
||
function PLUGIN:PlayerSay(client, text, teamChat)
|
||
local chatType = teamChat and "[TEAM]" or "[ALL]"
|
||
local message = string.format("%s %s: %s", chatType, client:Nick(), text)
|
||
|
||
self:AddLog("CHAT_MESSAGE", message, client, {
|
||
text = text,
|
||
team = teamChat
|
||
})
|
||
end
|
||
|
||
-- Создание персонажа
|
||
function PLUGIN:OnCharacterCreated(client, character)
|
||
local message = string.format("%s создал персонажа '%s'",
|
||
client:Nick(), character:GetName())
|
||
|
||
self:AddLog("CHARACTER_CREATE", message, client, {
|
||
characterID = character:GetID(),
|
||
characterName = character:GetName()
|
||
})
|
||
end
|
||
|
||
-- Удаление персонажа
|
||
function PLUGIN:PreCharacterDeleted(client, character)
|
||
local message = string.format("%s удалил персонажа '%s'",
|
||
client:Nick(), character:GetName())
|
||
|
||
self:AddLog("CHARACTER_DELETE", message, client, {
|
||
characterID = character:GetID(),
|
||
characterName = character:GetName()
|
||
})
|
||
end
|
||
|
||
-- Смена персонажа
|
||
function PLUGIN:PlayerLoadedCharacter(client, character, oldCharacter)
|
||
if oldCharacter then
|
||
local message = string.format("%s сменил персонажа с '%s' на '%s'",
|
||
client:Nick(), oldCharacter:GetName(), character:GetName())
|
||
|
||
self:AddLog("CHARACTER_SWITCH", message, client, {
|
||
oldCharacter = oldCharacter:GetName(),
|
||
newCharacter = character:GetName()
|
||
})
|
||
end
|
||
end
|
||
|
||
-- Покупка предмета
|
||
function PLUGIN:CharacterVendorTraded(client, vendor, x, y, invID, price, isSell)
|
||
local action = isSell and "продал" or "купил"
|
||
local message = string.format("%s %s предмет за %d",
|
||
client:Nick(), action, price)
|
||
|
||
self:AddLog("ITEM_PURCHASE", message, client, {
|
||
vendor = vendor,
|
||
price = price,
|
||
sell = isSell
|
||
})
|
||
end
|
||
|
||
-- Использование предмета
|
||
function PLUGIN:OnItemTransferred(item, curInv, inventory)
|
||
if item.player then
|
||
local message = string.format("%s переместил предмет '%s'",
|
||
item.player:Nick(), item:GetName())
|
||
|
||
self:AddLog("INVENTORY", message, item.player, {
|
||
item = item:GetName(),
|
||
itemID = item:GetID()
|
||
})
|
||
end
|
||
end
|
||
|
||
-- Инициализация при загрузке плагина
|
||
function PLUGIN:InitPostEntity()
|
||
self:InitializeLogSystem()
|
||
self:AddLog("ADMIN_COMMAND", "Система логов инициализирована")
|
||
end
|
||
|
||
-- ======================
|
||
-- КОМАНДЫ HELIX
|
||
-- ======================
|
||
|
||
-- Регистрация CAMI разрешения
|
||
if CAMI then
|
||
CAMI.RegisterPrivilege({
|
||
Name = "Helix - View Server Logs",
|
||
MinAccess = "admin",
|
||
Description = "Позволяет просматривать логи сервера"
|
||
})
|
||
|
||
CAMI.RegisterPrivilege({
|
||
Name = "Helix - Clear Server Logs",
|
||
MinAccess = "superadmin",
|
||
Description = "Позволяет очищать логи сервера"
|
||
})
|
||
|
||
CAMI.RegisterPrivilege({
|
||
Name = "Helix - Use Handcuffs",
|
||
MinAccess = "admin",
|
||
Description = "Позволяет использовать наручники"
|
||
})
|
||
end
|
||
|
||
-- Команда для просмотра логов
|
||
ix.command.Add("ViewLogs", {
|
||
description = "Открыть панель просмотра логов сервера",
|
||
OnCheckAccess = function(self, client)
|
||
return HasLogAccess(client)
|
||
end,
|
||
OnRun = function(self, client)
|
||
-- Отправляем команду на открытие меню на клиенте
|
||
net.Start("ixServerLogs_OpenMenu")
|
||
net.Send(client)
|
||
|
||
return "@serverLogsOpened"
|
||
end
|
||
})
|
||
|
||
-- Альтернативная команда
|
||
ix.command.Add("ServerLogs", {
|
||
description = "Открыть панель просмотра логов сервера",
|
||
OnCheckAccess = function(self, client)
|
||
return HasLogAccess(client)
|
||
end,
|
||
OnRun = function(self, client)
|
||
-- Отправляем команду на открытие меню на клиенте
|
||
net.Start("ixServerLogs_OpenMenu")
|
||
net.Send(client)
|
||
|
||
return "@serverLogsOpened"
|
||
end
|
||
})
|
||
|
||
-- Команда для очистки логов
|
||
ix.command.Add("ClearLogs", {
|
||
description = "Очистить все логи сервера",
|
||
superAdminOnly = true,
|
||
OnCheckAccess = function(self, client)
|
||
-- Проверка через CAMI
|
||
if CAMI then
|
||
return CAMI.PlayerHasAccess(client, "Helix - Clear Server Logs", function(allowed)
|
||
return allowed
|
||
end)
|
||
end
|
||
-- Fallback на стандартную проверку суперадмина
|
||
return client:IsSuperAdmin()
|
||
end,
|
||
OnRun = function(self, client)
|
||
PLUGIN.logs = {}
|
||
PLUGIN:AddLog("ADMIN_COMMAND", client:Nick() .. " очистил логи сервера", client)
|
||
return "@serverLogsCleared"
|
||
end
|
||
})
|
||
|
||
-- Команда для просмотра последних логов в консоли
|
||
ix.command.Add("LastLogs", {
|
||
description = "Показать последние 20 логов в консоли",
|
||
adminOnly = true,
|
||
arguments = {
|
||
ix.type.number,
|
||
},
|
||
argumentNames = {"количество"},
|
||
OnCheckAccess = function(self, client)
|
||
-- Проверка через CAMI
|
||
if CAMI then
|
||
return CAMI.PlayerHasAccess(client, "Helix - View Server Logs", function(allowed)
|
||
return allowed
|
||
end)
|
||
end
|
||
return client:IsAdmin()
|
||
end,
|
||
OnRun = function(self, client, count)
|
||
count = math.Clamp(count or 20, 1, 100)
|
||
|
||
local logs = PLUGIN:GetLogs({limit = count})
|
||
|
||
client:ChatPrint("=== Последние " .. #logs .. " логов ===")
|
||
for i, log in ipairs(logs) do
|
||
local categoryName = PLUGIN.LOG_CATEGORIES[log.category].name
|
||
local targetInfo = log.target and (" [" .. log.target.name .. "]") or ""
|
||
client:ChatPrint(string.format("[%s][%s]%s %s",
|
||
log.timeString, categoryName, targetInfo, log.message))
|
||
end
|
||
|
||
return "@serverLogsDisplayed", #logs
|
||
end
|
||
})
|
||
|
||
-- Команда для поиска в логах
|
||
ix.command.Add("SearchLogs", {
|
||
description = "Найти логи по тексту или игроку",
|
||
adminOnly = true,
|
||
arguments = {
|
||
ix.type.text,
|
||
},
|
||
argumentNames = {"поисковый запрос"},
|
||
OnCheckAccess = function(self, client)
|
||
-- Проверка через CAMI
|
||
if CAMI then
|
||
return CAMI.PlayerHasAccess(client, "Helix - View Server Logs", function(allowed)
|
||
return allowed
|
||
end)
|
||
end
|
||
return client:IsAdmin()
|
||
end,
|
||
OnRun = function(self, client, search)
|
||
local logs = PLUGIN:GetLogs({search = search, limit = 50})
|
||
|
||
client:ChatPrint("=== Найдено логов: " .. #logs .. " ===")
|
||
for i, log in ipairs(logs) do
|
||
local categoryName = PLUGIN.LOG_CATEGORIES[log.category].name
|
||
local targetInfo = log.target and (" [" .. log.target.name .. "]") or ""
|
||
client:ChatPrint(string.format("[%s][%s]%s %s",
|
||
log.timeString, categoryName, targetInfo, log.message))
|
||
end
|
||
|
||
return "@serverLogsSearchResults", #logs
|
||
end
|
||
})
|