Проведи рефакторинг и профилирование кода, убери debug выводы и напиши полный для CL и SV
```lua
-- Клиентский Скрипт: lua/autorun/client/cl_notification.lua
if CLIENT then
    -- Константы
    local NOTIFICATION_WIDTH, NOTIFICATION_HEIGHT = 300, 100
    local ANIMATION_DURATION = 0.5 -- секунды
    local DISPLAY_DURATION = 5 -- секунды
    local DEFAULT_MODEL = "models/player/kleiner.mdl"
    local NOTIFICATION_BG_COLOR = Color(50, 50, 50, 200)
    local TEXT_COLOR = Color(255, 255, 255)
    local TEXT_FONT = "DermaDefault"
    -- Очередь уведомлений и список активных уведомлений
    local notificationQueue = {}
    local activeNotifications = {}
    -- Максимальное количество одновременно отображаемых уведомлений
    local MAX_ACTIVE_NOTIFICATIONS = 5
    -- Функция для получения позиций уведомления
    local function GetNotificationPosition(index)
        local padding = 10
        local y = padding + (NOTIFICATION_HEIGHT + padding) * (index - 1)
        return ScrW() - NOTIFICATION_WIDTH - padding, y
    end

    -- Проверка валидности пути к модели
    local function IsValidModelPath(modelPath)
        return util.IsValidModel(modelPath) and file.Exists(modelPath, "GAME")
    end

    -- Удаление фрейма
    local function RemoveFrame(frame, index)
        if not IsValid(frame) then return end
        -- Отключаем анимации на панели перед её удалением
        if frame.anim then
            frame.anim:Stop()
            frame.anim = nil
        end

        frame:Remove()
        activeNotifications[index] = nil
        -- Обновление позиций оставшихся уведомлений
        for i = index, #activeNotifications do
            local notif = activeNotifications[i]
            if IsValid(notif.frame) then
                local targetX, targetY = GetNotificationPosition(i)
                notif.frame:MoveTo(targetX, targetY, ANIMATION_DURATION, 0, 3)
            end
        end
    end

    -- Настройка панели модели
    local function SetupModelPanel(frame, modelPath)
        local mdl = vgui.Create("DModelPanel", frame)
        mdl:SetPos(10, 10)
        mdl:SetSize(80, 80)
        local validModel = IsValidModelPath(modelPath) and modelPath or DEFAULT_MODEL
        mdl:SetModel(validModel)
        if not IsValid(mdl.Entity) or mdl.Entity:GetModel() ~= validModel then mdl:SetModel(DEFAULT_MODEL) end
        local min, max = mdl.Entity:GetRenderBounds()
        local size = math.max(max.x - min.x, max.y - min.y, max.z - min.z)
        mdl:SetFOV(45)
        mdl:SetCamPos(Vector(size, size, size))
        mdl:SetLookAt((min + max) * 0.5)
        -- Вращение модели
        function mdl:LayoutEntity(entity)
            entity:SetAngles(Angle(0, CurTime() * 30, 0))
        end
    end

    -- Настройка текстового лейбла
    local function SetupTextLabel(frame, text)
        local lbl = vgui.Create("DLabel", frame)
        lbl:SetPos(100, 10)
        lbl:SetSize(NOTIFICATION_WIDTH - 110, NOTIFICATION_HEIGHT - 20)
        lbl:SetText(text)
        lbl:SetTextColor(TEXT_COLOR)
        lbl:SetFont(TEXT_FONT)
        lbl:SetWrap(true)
        lbl:SetAutoStretchVertical(true)
        lbl:SetContentAlignment(5) -- Центрирование текста
    end

    -- Настройка фрейма уведомления
    local function SetupFrame(text, modelPath, index)
        local frame = vgui.Create("DPanel")
        frame:SetSize(NOTIFICATION_WIDTH, NOTIFICATION_HEIGHT)
        frame:SetPos(ScrW(), 0) -- Временная позиция; y будет установлен позже
        frame:ParentToHUD()
        frame:SetZPos(1000)
        function frame:Paint(w, h)
            draw.RoundedBox(8, 0, 0, w, h, NOTIFICATION_BG_COLOR)
        end

        SetupTextLabel(frame, text)
        SetupModelPanel(frame, modelPath)
        return frame
    end

    -- Основная функция создания уведомления
    local function CreateNotification(text, modelPath)
        -- Создание уведомления и добавление его в очередь
        local notification = {
            text = text,
            modelPath = modelPath
        }

        table.insert(notificationQueue, notification)
    end

    -- Обработка показа уведомлений из очереди
    local function ProcessQueue()
        while #activeNotifications < MAX_ACTIVE_NOTIFICATIONS and #notificationQueue > 0 do
            local notification = table.remove(notificationQueue, 1)
            local index = #activeNotifications + 1
            local frame = SetupFrame(notification.text, notification.modelPath, index)
            local targetX, targetY = GetNotificationPosition(index)
            frame:SetPos(ScrW(), targetY)
            frame:MoveTo(targetX, targetY, ANIMATION_DURATION, 0, 3, function()
                -- Запуск таймера для скрытия уведомления
                timer.Simple(DISPLAY_DURATION, function()
                    if IsValid(frame) then
                        frame:MoveTo(ScrW(), targetY, ANIMATION_DURATION, 0, 3, function()
                            if IsValid(frame) then
                                local currentIndex = table.KeyFromValue(activeNotifications, {
                                    frame = frame
                                })

                                if currentIndex then RemoveFrame(frame, currentIndex) end
                            end
                        end)
                    end
                end)
            end)

            -- Добавление в список активных уведомлений
            table.insert(activeNotifications, {
                frame = frame
            })
        end
    end

    -- Обработка сетевого сообщения для показа уведомления
    net.Receive("ShowNotification", function()
        local text = net.ReadString()
        local model = net.ReadString()
        CreateNotification(text, model)
        ProcessQueue()
    end)

    -- Консольная команда для тестирования уведомления на клиенте
    concommand.Add("show_notification_client", function()
        CreateNotification("Это тестовое уведомление.", "models/player/alyx.mdl")
        ProcessQueue()
    end)
end
```
```lua
-- Серверный Скрипт: lua/autorun/server/notification_server.lua
if SERVER then
    -- Регистрация сетевой строки для уведомлений
    util.AddNetworkString("ShowNotification")
    -- Константы
    local DEFAULT_MODEL = "models/player/kleiner.mdl" -- Согласованный дефолтный модель
    local VALID_MODEL_PREFIX = "^models/"
    -- Белый список моделей (дополните по необходимости)
    local WHITELISTED_MODELS = {
        ["models/player/kleiner.mdl"] = true,
        ["models/player/alyx.mdl"] = true,
        ["models/player/mossman.mdl"] = true,
        ["models/player/barney.mdl"] = true,
    }

    -- Добавьте больше разрешённых моделей здесь
    -- Вспомогательная функция для отправки сообщений источнику
    local function NotifySender(ply, message)
        if IsValid(ply) then
            ply:ChatPrint(message)
        else
            print(message)
        end
    end

    -- Функция для отправки уведомления игроку
    local function SendNotification(ply, text, modelPath)
        net.Start("ShowNotification")
        net.WriteString(text)
        net.WriteString(modelPath)
        net.Send(ply)
    end

    -- Функция для проверки формата Steam ID
    local function IsValidSteamID(sid)
        return string.match(sid, "^STEAM_[0-5]:%d:%d+$") ~= nil
    end

    -- Функция для проверки, находится ли модель в белом списке
    local function IsWhitelistedModel(modelPath)
        return WHITELISTED_MODELS[modelPath] or false
    end

    -- Консольная команда для отправки уведомлений
    -- Использование: show_notification_server <steam_id> <текст> [<путь_к_модели>]
    concommand.Add("show_notification_server", function(ply, cmd, args)
        -- Проверка прав администратора, если команда выполняется игроком
        if IsValid(ply) and not ply:IsAdmin() then
            ply:ChatPrint("У вас нет прав для использования этой команды.")
            return
        end

        -- Минимальное количество аргументов: Steam ID и текст
        if #args < 2 then
            local usageMsg = "Использование: show_notification_server <steam_id> <текст> [<путь_к_модели>]"
            NotifySender(ply, usageMsg)
            return
        end

        local steamID = args[1]
        -- Проверка формата Steam ID
        if not IsValidSteamID(steamID) then
            local errorMsg = "Неверный формат Steam ID. Пример: STEAM_0:1:123456"
            NotifySender(ply, errorMsg)
            return
        end

        -- Извлечение текста и опционального пути к модели
        table.remove(args, 1) -- Удаляем Steam ID из args
        local model = DEFAULT_MODEL
        -- Проверяем, является ли последний аргумент путём к модели
        if string.find(args[#args], VALID_MODEL_PREFIX) then
            local potentialModel = args[#args]
            if IsWhitelistedModel(potentialModel) then
                model = potentialModel
            else
                local warnMsg = "Указанная модель не разрешена. Используется модель по умолчанию."
                NotifySender(ply, warnMsg)
            end

            table.remove(args, #args)
        end

        local text = table.concat(args, " ")
        -- Находим игрока по Steam ID
        local target = player.GetBySteamID(steamID)
        if IsValid(target) and target:IsConnected() then
            SendNotification(target, text, model)
            local successMsg = "Уведомление отправлено игроку " .. target:Nick()
            NotifySender(ply, successMsg)
        else
            local notFoundMsg = "Игрок с Steam ID \"" .. steamID .. "\" не найден или не онлайн."
            NotifySender(ply, notFoundMsg)
        end
    end)
end
```