Различные функции и информация

Dragokas

Меценат
Сообщения
184
Реакции
145
7. Различные функции и информация:

Hammer и Authoring Tools:
  • Для L4D/2 перейдите в клиент Steam > Библиотеки > Снимите галочку "Показать только готовые к запуску игры" (в старых версиях - это раздел "Инструменты"), найдите в списке и скачайте L4D/2 Authoring Tools. Он включает в себя Model Viewer (просмотрщик моделей) - полезен для определения индексов анимации и точек крепления, используемых для фишек вроде "SetParent". Вы можете распаковать модель .mdl в папку вашей игры /models/ чтобы отрыть её в этом инструменте.
  • CS:GO hammer доступен только после покупки обновления с Prime статусом на странице магазина Steam. Ссылки: здесь и здесь.

  • Расширение Stripper:Source- Вместо декомпиляции карты, с помощью этого инструмента можно сделать дамп данных карты и уже в нём смотреть, что и как работает.
    • Я использовал это и hammer для воспроизведения через плагин различных функций, реализованных на картах для L4D2.
    • Stripper также полезен для изменения сущностей на картах. Почитайте его тему для больших подробностей.

Партикли (так называемые частицы, Particles):
  • В хаммере (по крайней мере в L4D2) вы можете увидеть частицы, указав их в сущности "info_particle_system", сделав двойной клик на "Particle System Name" в списке keyvalues. Это откроет "Браузер частиц". Редактор частиц доступен через "L4D Authoring Tools" -> L4D (Tools Mode).
  • Также есть [Batch] Пакетный распаковщик частиц & плагин тестирования и руководство от Dragokas.
  • Некоторые или большинство из игр на движке source также включают внутри-игровой Редактор частиц (см. по ссылке, как его включить), полезен для отображения и создания сторонних частиц. Вы можете воспользоваться функцией SourceMod-а AddFileToDownloadsTable для передачи клиентам сторонних моделей, звуков, частиц и т.д.
  • Чтобы пре-кешировать частицы используйте следующий stock и вызывайте его в OnMapStart();
PHP:
public void OnMapStart()
{
    PrecacheParticle("flare_burning");
}

int PrecacheParticle(const char[] sEffectName)
{
    static int table = INVALID_STRING_TABLE;

    if( table == INVALID_STRING_TABLE )
    {
        table = FindStringTable("ParticleEffectNames");
    }

    int index = FindStringIndex(table, sEffectName);
    if( index == INVALID_STRING_INDEX )
    {
        bool save = LockStringTables(false);
        AddToStringTable(table, sEffectName);
        LockStringTables(save);
        index = FindStringIndex(table, sEffectName);
    }

    return index;
}

AddFileToDownloadsTable: (сторонний контент)​

Свойства сущности: (Prop_Data и Prop_Send)​
  • NetProps и DataMaps являются игровыми переменными, используемыми сущностями для хранения данных о себе. NetProps передаются клиентам, тогда как DataMaps доступны только для сервера.
  • Их можно читать с помощью: GetEntProp, GetEntPropArraySize, GetEntPropEnt (для получения сущности или индекса клиента из свойства сущности), GetEntPropFloat, GetEntPropFloat, GetEntPropVector и изменять через соответствующие SetEntProp* функции. HasEntProp позволит вам проверить, существует ли у сущности указанное вами имя свойства.
  • SourceMod предоставляет несколько команд для сбора полезной информации. Чтобы просмотреть полный список, введите find sm_dump в консоль сервера.
  • Введите следующие команды в вашу серверную консоль для генерации и сохранения отчётов в корневую папку игры (рядом с addons, bin, cfg, maps):
  • Команды для дампа всех свойств netprops и datamaps у всех сущностей:
    - Формат лога "datamaps": "имя свойства" ("смещение") ("тип") ("размер") - "KeyValue" (применяется в функциях DispatchKeyValue*).
    sm_dump_netprops_xml netprops.xml
    sm_dump_netprops netprops.txt
    sm_dump_datamaps datamaps.txt
  • Имена классов сущностей выводятся в формате: серверное имя класса (GetEntityNetClass) - имя класса сущности (GetEdictClassname).
    sm_dump_classes classes.txt
  • Список временных сущностей (TempEnts) и их формат.
    sm_dump_teprops teprops.txt

События:
  • Игра возбуждает события, которые затем плагины могут перехватить (хукнуть) для выполнения различных задач, когда наступают специфические условия, вызывающие эти события.
  • Events (SourceMod Scripting) - Подробности о том, как искать, хукать, создавать, блокировать и изменять события.
  • Game Events (Source) - Список разнообразных событий, общих и специфических для конкретной игры событий (Некоторые данные могут отсутствовать или быть устаревшими).
  • Вы можете воспользоваться GCFScape, чтобы открыть ваши игровые файлы .VPK и извлечь оттуда различные .res файлы для поиска событий ("events") в папке resource.

Звуковые хуки:
  • Звуковые хуки могут изменять громкость, воспроизводимый файл или блокировать звуки.
PHP:
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>

bool g_bBlockSound;

public void OnPluginStart()
{
    // ПРЕДУПРЕЖДЕНИЕ:  Избегайте хука более, чем один раз, хукайте только единожды.
    // Вы можете, к примеру, использовать статический или глобальный bool, допустим g_bHooked, чтобы избежать множественный хуков звука.
    // Вы можете хукать, где угодно. Хук не обязательно должен быть в OnPluginStart.

    // Большинство звуков воспроизводятся через это:
    AddNormalSoundHook(SoundHook);

    // Однако, некоторые звуки могут воспроизводиться только здесь:
    AddAmbientSoundHook(AmbientHook));
}

public void OnPluginEnd()
{
    // Вам не нужно снимать хук в OnPluginEnd, просто показываю, что вы можете это сделать, если вам больше не нужен хук.
    RemoveNormalSoundHook(SoundHook);
    RemoveAmbientSoundHook(AmbientHook);
}

// Слежение за броском гранат.
public void OnEntityCreated(int entity, const char[] classname)
{
    if( strncmp(classname, "molotov_projectile", 13) == 0 )
    {
        SDKHook(entity, SDKHook_SpawnPost, SpawnPost);
    }
}

public void SpawnPost(int entity)
{
    RequestFrame(OnNextFrame);
    g_bBlockSound = true;
}

public void OnNextFrame()
{
    g_bBlockSound = false;
}

public Action SoundHook(int clients[MAXPLAYERS], int &numClients, char sample[PLATFORM_MAX_PATH], int &entity, int &channel, float &volume, int &level, int &pitch, int &flags, char soundEntry[PLATFORM_MAX_PATH], int &seed)
{
    // Поскольку SoundHook вызывается для каждого воспроизводимого звука, лучше всего избегать сравнения строки, на сколько это возможно
    if( g_bBlockSound )
    {
        // Блокируем звук, когда бросается молотов
        if( strcmp(sample, "weapons/molotov/fire_loop_1.wav") == 0 )
        {
            return Plugin_Handled;
        }
    }

    // Уменьшаем громкость взрыва
    if( strcmp(sample, "weapons/molotov/molotov_detonate_3.wav") == 0 )
    {
        volume = 0.5;
        return Plugin_Changed;
    }

    // Без изменений
    return Plugin_Continue;
}

public Action AmbientHook(char sample[PLATFORM_MAX_PATH], int &entity, float &volume, int &level, int &pitch, float pos[3], int &flags, float &delay)
{
    // Тоже самое, что и выше, только для звуков окружения (Ambient Sounds).
}

SetTransmit: (Прячет модели от отдельных игроков):​
  • SDKHook_SetTransmit может быть использован, чтобы спрятать от клиентов модели, частицы и некоторые другие типы сущностей (например, light_dynamic).
  • Попытка спрятать частицы может не сработать в некоторых играх или например, может сработать только на Windows. Это ограничения движка.
  • Попытка спрятать некоторые сущности может не сработать, например, prop_glowing_object в L4D1.
  • Инфа от asherkin: CBaseEntity::SetTransmit является очень горячей функцией, поэтому у движка есть множество оптимизаций, чтобы предотвращать излишние её вызовы. Обычно, по этому пути идут сущности с флагами FL_EDICT_FULLCHECK, FL_EDICT_ALWAYS и FL_EDICT_PVSCHECK. Для полного понимания, можно обратиться к ключевой логике в CServerGameEnts::CheckTransmit из SDK.
  • Два примера, для пропов и частиц:

Модели:
PHP:
bool g_bShowProp[MAXPLAYERS+1];
public Action CmdProp(int client, int args)
{
    // Предположим, что мы создали сущность "prop", но нам хочется показать её только конкретным клиентам
    int entity = CreateEntityByName("prop_dynamic");
    // Остальные вещи для сущности. SetEntityModel() и т.д. Опускаем здесь, для краткости примера.

    // Теперь мы хукаем её. Функция обратного вызова (колбэк) Hook_SetTransmit вызывается с каждым фреймом, поэтому здесь внутри лучше использовать простые расчёты
    SDKHook(entity, SDKHook_SetTransmit, Hook_SetTransmit);

    // Существуют разные способы, чтобы это сделать. Советую посмотреть другие плагины в качестве примеров.
    // Мы собираемся сохранить здесь значение, чтобы определять, можно ли клиенту видеть сущность или нет.
    g_bShowProp[client] = true;
}

public Action Hook_SetTransmit(int entity, int client)
{
    // Проверяем, разрешено ли клиенту видеть сущность
    if( g_bShowProp[client] == true )
        return Plugin_Continue; // Это разрешит сущности быть видимой для клиента.
    return Plugin_Handled; // Это спрячет её от клиента, если клиенту не разрешается видеть её.
}

Частицы:
PHP:
bool g_bShowProp[MAXPLAYERS+1];
public Action CmdProp(int client, int args)
{
    // Предположим, мы создали частицу, но желаем отобразить её только для конкретных игроков
    int entity = CreateEntityByName("info_particle_system");
    // Остальные свойства сущности. DispatchSpawn() и т.д. Опускаем, чтобы сократить пример.

    SDKHook(entity, SDKHook_SetTransmit, Hook_SetTransmit);

    g_bShowProp[client] = true;
}

public Action Hook_SetTransmit(int entity, int client)
{
    // Проверка, позволено ли клиенту видеть проп
    if( g_bShowProp[client] == true )
        return Plugin_Continue; // Это разрешит пропу быть видимым для клиента

    // Следующие две строки используются, чтобы прятать частицу
    // Это, вероятно, может заработать только в конкретных играх, либо только на серверах Windows
    if( GetEdictFlags(entity) & FL_EDICT_ALWAYS )
        SetEdictFlags(entity, GetEdictFlags(entity) &~ FL_EDICT_ALWAYS);

    return Plugin_Handled;
}

RequestFrame: (выполнение кода с задержкой времени на 1 фрейм)​
  • CreateTimer() имеет минимальное значение в 0.1 секунду. Для всего, что быстрее (например, следующий фрейм игры) лучше использовать RequestFrame().
  • Когда вы определяете снаряды (projectiles) в OnEntityCreated() и используете SDKHook_SpawnPost(), данные о скорости здесь всё ещё не инициализированы. Вот где я использую RequestFrame() для получения действительных данных.
PHP:
// Слежение за броском гранат
public void OnEntityCreated(int entity, const char[] classname)
{
    if( strcmp(classname, "molotov_projectile") == 0 )
    {
        // Ждем, пока сущность полностью заспавнится (появится)
        SDKHook(entity, SDKHook_SpawnPost, SpawnPost);
    }
}

public void SpawnPost(int entity)
{
    // 1 фрейм требуется для получения скорости
    RequestFrame(OnNextFrame, EntIndexToEntRef(entity));
}

public void OnNextFrame(int entity)
{
    // Верифицируем сущность
    if( EntRefToEntIndex(entity) == INVALID_ENT_REFERENCE || !IsValidEntity(entity) )
        return;

    // Теперь скорость действительна и не равна 0,0,0
    float vPos[3];
    GetEntPropVector(entity, Prop_Send, "m_vInitialVelocity", vPos);
}

  • Переключение игроков в команду Зрителей сразу же после подключения. Это предотвращает от каких-либо задержек, из-за которых их могло бы успеть заспавнить сперва где-либо ещё (спасибо "Maxximou5" и "Desktop" за пример):
PHP:
#include <cstrike>

public void OnPluginStart()
{
    HookEvent("player_connect_full", Event_PlayerConnectFull);
}

public void Event_PlayerConnectFull(Event event, const char[] name, bool dontBroadcast)
{
    int client = GetClientOfUserId(event.GetInt("userid"));
    RequestFrame(Frame_ChangeTeam, client);
}

public void Frame_ChangeTeam(any client)
{
    ChangeClientTeam(client, CS_TEAM_SPECTATOR);
}

Разное:
  • Цвет текста (инклуды): Colors.inc, stamm-colors.inc, morecolors.inc, multicolors.inc, colorvariables.inc, colorsLib.inc
  • SteamID типы: Steam2, Steam3 & Steam64. Используйте GetClientAuthId для получения нужного вам типа. Хороши для хранения в файл/базе данных для восстановления после перезагрузки.
  • KeyValue конфиги - Они необходимы для многих вещей. Настройки данных, чтение, сохранение. Например, KeyValues SourceMod Scripting.
  • SMCParser - Подобны KeyValue конфигам, но не требуют заранее знать имя ключа. Примеры см. в папке со скриптами SM scripting/admin-flatfile. Или в моих VScript Replacer, Info Editor или Neon Beams, как наиболее простая версия. Прим. переводчика: является более надёжным инструментом, совместим с форматом KeyValues файлов, но в отличии от последнего поддерживает многострочные комментарии).
  • Client Preferences (предпочтения клиента) - это куки, которые используются для хранения клиентских данных. [Tutorial] ClientPrefs. Хранение простых настроек для каждого из клиентов. Прим. переводчика: по своей сути представляют из себя записи в базе данных, хранящейся на сервере (файл data/sqlite/clientprefs-sqlite.sq3).
  • GCFScape - Для открытия VPK и получения файлов, к примеру "modevents.res" в качестве источника для событий, используемых игрой (другие источники по большей части устарели). Также для открытия .BSP и извлечения ресурсов карты.
  • VProf - Для проверки производительности плагинов. Используйте только в тестовой среде, а не на живом сервере, т.к. эта проверка может быть слишком интенсивной.
  • SM profiler API - Для запуска проверок скорости работы (бенчмаркинга) определённых участков вашего плагина.
  • Timers - Можно использовать для задержки задач, например, сообщений с приветствием, или для повторения чего-либо несколько раз в течении определённого интервала. См. сообщение со множеством примеров правильного использования.
  • Translations - Используется для многоязычной поддержки, когда на экран отображаются сообщения или перевод в менюшках и пр.


Информация:
  • edict могут иметь индексы только в пределах от 0 до 2048. Больше 2048 имеют не передающиеся по сети сущности (non-networked entities), которые может увидеть только сервер (например, сущность logic_case).
    • Новые игры имеют более высокие лимиты.
    • CS:GO может использовать до 4096 под edicts (Я предполагаю, что 4097 - 8192 - выделены для не передающихся по сети сущностей).
    • Garrys Mod очевидно поддерживает до 8192 под edict-ы.
  • IsValidEdict() возвращает false для non-networked сущностей. Для таких, используйте IsValidEntity().
  • FindEntityByClassname() для non-networked сущностей возвращает ссылку на сущность (к примеру, номер сущности будет чем-то вроде: -2036529131).
    - Вы можете преобразовать её обратно в индекс сущности с помощью EntRefToEntIndex() и получить значение между 2049-4096. Однако, если вы не планируете использовать его в качестве индекса для массива, то лучше ограничиться использованием ссылки.
  • Format - Используйте при форматировании строки, когда буфер назначения является также одним из параметров.
    PHP:
    Format(buffer, sizeof buffer, "%i %s", someInt, buffer);
  • FormatEx - Используйте, когда форматируемый и целевой буферы НЕ совпадают (она быстрее чем Format).
    PHP:
    FormatEx(buffer, sizeof buffer, "%i", someInt);
  • strcopy - Используйте для копирования строки, не использующей правила форматирования.
    PHP:
    strcopy(buffer, sizeof buffer, "Some string");
  • StrCat - Используйте для добавления в конец целевой строки, без необходимости в форматировании.
    PHP:
    StrCat(buffer, sizeof buffer, "this text is appended to buffer");
  • net_showevents 2 - отобразит на серверной консоли все игровые события
  • report_entities - считает кол-во одинаковых классов энтити на карте и выводит отчёт в серверную консоль
  • cl_showents клиентский квар - выводит список сущностей и их индексы (требует sv_cheats 1).
  • cl_showpos 1 клиентский квар - для открытия мини-панели, отслеживающей вашу текущую позицию, угол, скорость.
  • +posedebug клиентский квар - показывает имена поз / анимаций.
  • soundinfo - отображает список звуков, которые воспроизводятся в данный момент.
  • listmodels - список моделей на этой карте.
  • net_graph клиентский квар - позволяет вам увидеть графики fps, ping, lerp, нагрузку на сеть и т.п. Больше инфы.
    • net_graph 4 - рекомендован.
    • Вот примеры биндов, которые я использую. Когда нажимаешь на Tab для открытия табло с игроками, то одновременно отображается и net_graph:
    PHP:
    bind tab +tabscore;
     alias +tabscore "+showscores; net_graph 4";
     alias -tabscore "-showscores; net_graph 0";
     net_graphpos 0 // default 1; Left = 0; Right = 1; Centre = 2
  • Клиентские квары для симуляции лагов (можно использовать для тестов). Требуется включённый sv_cheats:
    • net_fakejitter - фейковые колебания пинга (Jitter)
    • net_fakelag - лаг для всех входящий сетевых данных (включая петлю) на указанное кол-во миллисекунд.
    • net_fakeloss - Симулировать потерю пакетов в процентах (негативное число означает потерю 1/n пакетов)
    • net_droppackets - Потерять следующие N пакетов у клиента
    • net_chokeloop - Применить колебание на полосе пропускания для петлевых пакетов
  • sv_allow_wait_command - Серверный квар, разрешающий или запрещающий команду wait для клиентов, подключённых к серверу.
    - Это приведет к вылету клиента, если он однажды использовал команду wait, а сервер отключил её без перезапуска игры клиентом.
 
Последнее редактирование:

Kruzya

Здравствуй, юность в сапогах
Меценат
Сообщения
10,713
Реакции
8,862
SteamID типы: Steam2, Steam3 & Steam64. Используйте GetClientAuthId для получения нужного вам типа. Хороши для хранения в файл/базе данных для восстановления после перезагрузки.
Опять же, чисто моё ИМХО. Для сравнения/хранения лучше всего подходит GetSteamAccountID, поскольку функция возвращает представление SteamID в виде 32-битного инта, а 32-битный инт всяко проще искать/писать/сравнивать.

Ещё я бы отметил, что для функций, поддерживающих форматирование, пред-форматированное значение отдавать в лоб не стоит.
C-подобный:
// Какое-то заранее отформатированное значение.
// Ник нашего игрока - Hello %sworld.
char szCommandBuffer[64];
FormatEx(szCommandBuffer, sizeof(szCommandBuffer), "sm_kick \"%N\" No reason", iClient);

// Может вызвать ошибку о том, что нет 1-го аргумента форматирования.
ServerCommand(szCommandBuffer);

// Не вызовет ошибку, потому что не будет даже пытаться обработать
// переданную строку как "форматную".
ServerCommand("%s", szCommandBuffer);
 

ironman

Участник
Сообщения
378
Реакции
257
SDKHook_SetTransmit может быть использован, чтобы спрятать от клиентов модели, частицы и некоторые другие типы сущностей (например, light_dynamic).
добавлю: нельзя скрывать энтити, если у него есть info_particle_system как Parent. он по прежнему будет виден.
 
Сверху