From 53d3702c69509735b7b8a7774684c9bb7a8b6efa Mon Sep 17 00:00:00 2001 From: deidara Date: Fri, 1 May 2026 20:53:51 +0300 Subject: [PATCH] v2.0.0: refactor - models assigned by SteamID64 via config Major changes: - Added configs/vip_custom_models_locked.cfg (SteamID64 to model_id) - Command sm_vmodel_reload for hot reload - Removed model selection menu (!vmodel/!models/!agents) - Removed VIP-Core integration (item removed from VIP menu) - Removed ClientPrefs cookies and SQLite (config is single source of truth) Bug fixes: - Fixed null-check bug (error[0] != space instead of null terminator) - Removed DB vs Cookie race condition - Cleaned up buffer null-termination workarounds - author = deidara.dev --- README.md | 107 ++++++++++++------- configs/vip_custom_models_locked.cfg.example | 21 ++++ scripting/vip_custom_models.sp | Bin 24305 -> 14244 bytes 3 files changed, 87 insertions(+), 41 deletions(-) create mode 100644 configs/vip_custom_models_locked.cfg.example diff --git a/README.md b/README.md index d6af55a..c3d4fef 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,32 @@ # VIP Custom Models -Плагин VIP-моделей игроков для CS:GO серверов на SourceMod. Позволяет VIP-игрокам выбирать кастомные скины (агентов) через меню — выбор сохраняется через ClientPrefs и SQLite. +Плагин кастомных моделей игроков для CS:GO — модели **назначаются по SteamID64 через конфиг-файл**, без выбора через меню. Идеально для серверов, где админ хочет полностью контролировать кому какую модель давать. ## Функции -- До **128 кастомных моделей** из конфига -- Ограничение по **команде** (T / CT / любая) -- Ограничение по **VIP-группе** (через VIP Core API) -- Сохранение выбора через **ClientPrefs** (cookies) и **SQLite** (storage-local) +- До **128 кастомных моделей** из конфига `vip_custom_models.ini` +- Назначение **«SteamID64 → model_id»** через `vip_custom_models_locked.cfg` - Автоматическое применение модели при спавне и смене команды -- Корректное восстановление **стандартных рук** при сбросе модели +- Корректное восстановление **стандартных рук** для команды если кастомная модель не назначена или не подходит по команде +- Команда `sm_vmodel_reload` для перезагрузки конфига без рестарта плагина - Умная загрузка файлов модели: `.mdl`, `.vvd`, `.dx90.vtx` и сопутствующие -- Поддержка блочного и строкового форматов списка downloads -- Интеграция с VIP-меню через `VIP_RegisterFeature` +- Поддержка блочного и строкового форматов списка `downloads` +- Ограничение по **команде** (T / CT / ANY) — если игрок не на нужной команде, кастомная модель не применяется (используются дефолтные руки) ## Зависимости - [SourceMod](https://www.sourcemod.net/) 1.10+ -- [ClientPrefs](https://wiki.alliedmods.net/Client_Preferences_%28SourceMod%29) (входит в SourceMod) -- [VIP Core](https://github.com/R1KO/VIP-Core) — плагин VIP-системы ## Установка 1. Скомпилировать `scripting/vip_custom_models.sp` 2. Положить `.smx` в `addons/sourcemod/plugins/` -3. Положить `configs/vip_custom_models.ini` в `addons/sourcemod/configs/` -4. Загрузить модели на сервер (в папку `models/`) -5. Перезапустить сервер или загрузить плагин: `sm plugins load vip_custom_models` +3. Положить `configs/vip_custom_models.ini` (список доступных моделей) в `addons/sourcemod/configs/` +4. Положить `configs/vip_custom_models_locked.cfg` (назначения SteamID → модель) в `addons/sourcemod/configs/` +5. Загрузить файлы моделей в `models/` на сервере +6. Перезапустить сервер или загрузить плагин: `sm plugins load vip_custom_models` -## Конфиг +## Конфиг моделей Путь: `addons/sourcemod/configs/vip_custom_models.ini` @@ -39,50 +37,77 @@ { "agent_01" { - "name" "FBI Agent" - "model" "models/player/custom/fbi_agent.mdl" - "arms" "models/weapons/ct_arms_fbi_custom.mdl" - "team" "CT" - "groups" "*" - "downloads" "models/player/custom/fbi_agent.mdl;models/player/custom/fbi_agent.vvd" + "name" "FBI Agent" + "model" "models/player/custom/fbi_agent.mdl" + "arms" "models/weapons/ct_arms_fbi_custom.mdl" + "team" "CT" + "downloads" "models/player/custom/fbi_agent.mdl;models/player/custom/fbi_agent.vvd" } - "agent_02" + "elite_crew" { - "name" "Elite Crew" - "model" "models/player/custom/elite_crew.mdl" - "arms" "" - "team" "T" - "groups" "VIP;PREMIUM" + "name" "Elite Crew" + "model" "models/player/custom/elite_crew.mdl" + "arms" "" + "team" "T" "downloads" { - "1" "models/player/custom/elite_crew.mdl" - "2" "models/player/custom/elite_crew.vvd" - "3" "models/player/custom/elite_crew.dx90.vtx" + "1" "models/player/custom/elite_crew.mdl" + "2" "models/player/custom/elite_crew.vvd" + "3" "models/player/custom/elite_crew.dx90.vtx" } } } } ``` -### Поля модели - | Поле | Описание | |---|---| -| `name` | Отображаемое имя в меню | -| `model` | Путь к `.mdl` файлу | -| `arms` | Путь к модели рук (оставить пустым для стандартных) | -| `team` | `T`, `CT` или `ANY` | -| `groups` | VIP-группы через `;` или `*` для всех | -| `downloads` | Файлы для скачивания клиентами (строка через `;` или блок) | +| `name` | Отображаемое имя (для логов и админ-вывода) | +| `model` | Путь к `.mdl` файлу игрока | +| `arms` | Путь к модели рук (пустая строка = стандартные руки команды) | +| `team` | `T`, `CT` или `ANY` — на какой команде модель применяется | +| `downloads` | Файлы для FastDL (через `;` или блок) | + +## Конфиг назначений (новое в v2.0.0) + +Путь: `addons/sourcemod/configs/vip_custom_models_locked.cfg` + +``` +"VIP_LockedModels" +{ + "76561198012345678" "agent_01" + "76561198098765432" "elite_crew" +} +``` + +Ключ — SteamID64 игрока, значение — id модели (название секции из `vip_custom_models.ini`). + +После редактирования файла: +- Перезагрузи плагин: `sm plugins reload vip_custom_models` +- **ИЛИ** используй админ-команду `sm_vmodel_reload` (флаг `z` / ROOT) — без рестарта, моментально применяется ко всем игрокам в игре ## Команды | Команда | Доступ | Описание | |---|---|---| -| `!vmodel` / `sm_vmodel` | VIP | Открыть меню выбора модели | -| `!models` / `sm_models` | VIP | Открыть меню выбора модели | -| `!agents` / `sm_agents` | VIP | Открыть меню выбора модели | +| `sm_vmodel_reload` | Admin (флаг `z`) | Перезагрузить оба конфига без рестарта плагина | + +> Меню выбора моделей **удалено** — модели назначаются только через конфиг. ## Версия -`1.2.0` — Автор: OpenAI +`2.0.0` — Автор: deidara.dev + +### Changelog + +- **2.0.0** — Полный refactor: модели назначаются по SteamID64 через конфиг + - Добавлен `vip_custom_models_locked.cfg` с маппингом SteamID64 → model_id + - Удалено меню выбора моделей и команды `!vmodel`/`!models`/`!agents` + - Удалена интеграция с VIP-Core (пункт «Кастомные модели» больше не показывается в VIP-меню) + - Удалена ClientPrefs-cookies и SQLite-персистенция (конфиг — единственный источник истины) + - Команда `sm_vmodel_reload` для горячей перезагрузки + - **Багфиксы из 1.2.0:** + - Удалён баг с `error[0] != ' '` (был неправильный null-check, спамил в errors_*.log) + - Удалена race condition между загрузкой DB и cookies (DB+cookie больше не используются) + - Author = `"deidara.dev"` +- **1.2.0** — VIP-меню версия (legacy) diff --git a/configs/vip_custom_models_locked.cfg.example b/configs/vip_custom_models_locked.cfg.example new file mode 100644 index 0000000..e8141f5 --- /dev/null +++ b/configs/vip_custom_models_locked.cfg.example @@ -0,0 +1,21 @@ +// Назначение моделей игрокам по SteamID64 +// Формат: "" "" +// +// Чтобы узнать SteamID64 игрока: +// - в консоли сервера: status, потом https://steamid.io +// - либо команда `sm_who` если есть SourceMod-плагин для админов +// +// id_модели — это название секции из vip_custom_models.ini +// (например "agent_01", "elite_crew" — то что в кавычках перед { ) +// +// После правки файла перезагрузи плагин: +// sm plugins reload vip_custom_models +// ИЛИ +// sm_vmodel_reload (admin command, флаг z) + +"VIP_LockedModels" +{ + "76561198012345678" "agent_01" + "76561198098765432" "elite_crew" + "76561197960287930" "agent_01" +} diff --git a/scripting/vip_custom_models.sp b/scripting/vip_custom_models.sp index f4a124b0a3e24e0fff0f7869a5c0e965fc4b71d3..96069f9bb0dff84a92f82143f8261b2410fc0f3d 100644 GIT binary patch delta 4077 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