Склад функций SourcePawn

Kruzya

Участник
Сообщения
12,973
Реакции
10,927
  • Команда форума
  • #1
Тема для готовых функций на SourcePawn, которые можно просто вставить в свой код и использовать.
Функции сгруппированы по автору.
 
Последнее редактирование:

DarklSide

Участник
Сообщения
931
Реакции
468
1. Добавление статичных пунктов в нужную позицию, на всех страницах в меню

Начальный #пост под P.S.

PHP:
/**
* Добавление в меню статический(e) элемент(ы) под своим номером.
*
* @param menu                Menu Handle.
* @param startNum            Позиция, начиная с 0.
* @param sItem                Массив, One - Item information string, Two - item display string.
* @param maxSize            Общее кол-во элементов.
* @return                    True on success, false on failure.
* @error                    Invalid Handle or clear menu или если у элемента нет пары (One, Two, One)
*/
stock bool StaticNumInsertItem(Menu menu, int startNum, const char[][] sItem, int maxSize)
{
    if (menu != INVALID_HANDLE)
    {
        int iMenuItemCount = menu.ItemCount;
        if (iMenuItemCount > 0)
        {
            int iLog = maxSize % 2;
            int iMaxPageItems = GetMaxPageItems(menu.Style);
            //if (!iLog && startNum >= 0 && startNum < iMaxPageItems)
            if (!iLog && startNum >= 0)
            {
                #if defined DEBUG
                LogMessage("iMenuItemCount[%d]", iMenuItemCount);
                LogMessage("iMaxPageItems[%d]", iMaxPageItems);
                LogMessage("maxSize[%d]", maxSize);
                LogMessage("startNum[%d]", startNum);
                LogMessage("##### StartMenu #####");
                #endif
     
                //low
                for (int i = 0, j = startNum; i < maxSize; j++, i += 2)
                {
                    #if defined DEBUG
                    LogMessage("%s %s", sItem[i], sItem[i + 1]);
                    if (!menu.InsertItem(j, sItem[i], sItem[i + 1]))
                    {
                        return false;
                    }
                    #else
                    menu.InsertItem(j, sItem[i], sItem[i + 1]);
                    #endif
                }
     
                bool bExit = menu.ExitButton;
                bool bExitandBack = menu.ExitBackButton;
                if (bExitandBack)
                {
                    iMaxPageItems -= 4;
                }
                else if (bExit)
                {
                    iMaxPageItems -= 3;
                }
                else
                {
                    iMaxPageItems -= 3;
                }
     
                //middle
                int iCount;
                for (iCount = iMaxPageItems + startNum; iCount < menu.ItemCount; iCount += iMaxPageItems)
                {
                    for (int k = 0, j = iCount; k < maxSize; j++, k += 2)
                    {
                        #if defined DEBUG
                        if (!menu.InsertItem(j, sItem[k], sItem[k + 1]))
                        {
                            return false;
                        }
                        #else
                        menu.InsertItem(j, sItem[k], sItem[k + 1]);
                        #endif
                    }
                }
     
                #if defined DEBUG
                LogMessage("iCount[%d]_ItemCount[%d]", iCount, menu.ItemCount);
                #endif
     
                //hight
                if ((iCount - menu.ItemCount) < startNum) // && startNum < maxSize)
                {
                    for (int i = 0, j = menu.ItemCount; i < maxSize; j++, i += 2)
                    {
                        #if defined DEBUG
                        //LogMessage("%s %s", sItem[i], sItem[i + 1]);
                        if (!menu.AddItem(sItem[i], sItem[i + 1]))
                        {
                            return false;
                        }
                        #else
                        menu.AddItem(sItem[i], sItem[i + 1]);
                        #endif
                    }
                }
     
                return true;
            }
            else
            {
                LogError("Bad sItem", sItem);
            }
        }
        else
        {
            LogError("Clear Menu");
        }
    }
    return false;
}
PHP:
/**
* Добавление в меню статический(e) элемент(ы) под своим номером.
*
* @param menu                Menu Handle.
* @param startNum            Позиция, начиная с 0.
* @param sItem                Массив, One - Item information string, Two - item display string.
* @param maxSize            Общее кол-во элементов.
* @return                    True on success, false on failure.
* @error                    Invalid Handle or clear menu или если у элемента нет пары (One, Two, One)
*/
stock bool:StaticNumInsertItem(Handle:menu, startNum, const String:sItem[][], maxSize)
{
    if (menu != INVALID_HANDLE)
    {
        new iMenuItemCount = GetMenuItemCount(menu);
        if (iMenuItemCount > 0)
        {
            new iLog = maxSize % 2;
            new iMaxPageItems = GetMaxPageItems(GetMenuStyle(menu));
            //if (!iLog && startNum >= 0 && startNum < iMaxPageItems)
            if (!iLog && startNum >= 0)
            {
                #if defined DEBUG
                LogMessage("iMenuItemCount[%d]", iMenuItemCount);
                LogMessage("iMaxPageItems[%d]", iMaxPageItems);
                LogMessage("maxSize[%d]", maxSize);
                LogMessage("startNum[%d]", startNum);
                LogMessage("##### StartMenu #####");
                #endif
     
                //low
                for (new i = 0, j = startNum; i < maxSize; j++, i += 2)
                {
                    #if defined DEBUG
                    LogMessage("%s %s", sItem[i], sItem[i + 1]);
                    if (!InsertMenuItem(menu, j, sItem[i], sItem[i + 1]))
                    {
                        return false;
                    }
                    #else
                    InsertMenuItem(menu, j, sItem[i], sItem[i + 1]);
                    #endif
                }
     
                new bool:bExit = GetMenuExitButton(menu);
                new bool:bExitandBack = GetMenuExitBackButton(menu);
                if (bExitandBack)
                {
                    iMaxPageItems -= 4;
                }
                else if (bExit)
                {
                    iMaxPageItems -= 3;
                }
                else
                {
                    iMaxPageItems -= 3;
                }
     
                //middle
                new iCount;
                for (iCount = iMaxPageItems + startNum; iCount < GetMenuItemCount(menu); iCount += iMaxPageItems)
                {
                    for (new k = 0, j = iCount; k < maxSize; j++, k += 2)
                    {
                        #if defined DEBUG
                        if (!InsertMenuItem(menu, j, sItem[k], sItem[k + 1]))
                        {
                            return false;
                        }
                        #else
                        InsertMenuItem(menu, j, sItem[k], sItem[k + 1]);
                        #endif
                    }
                }
     
                #if defined DEBUG
                LogMessage("iCount[%d]_ItemCount[%d]", iCount, GetMenuItemCount(menu));
                #endif
     
                //hight
                if ((iCount - GetMenuItemCount(menu)) < startNum) // && startNum < maxSize)
                {
                    for (new i = 0, j = GetMenuItemCount(menu); i < maxSize; j++, i += 2)
                    {
                        #if defined DEBUG
                        //LogMessage("%s %s", sItem[i], sItem[i + 1]);
                        if (!AddMenuItem(menu, sItem[i], sItem[i + 1]))
                        {
                            return false;
                        }
                        #else
                        AddMenuItem(menu, sItem[i], sItem[i + 1]);
                        #endif
                    }
                }
     
                return true;
            }
            else
            {
                LogError("Bad sItem", sItem);
            }
        }
        else
        {
            LogError("Clear Menu");
        }
    }
    return false;
}

Примерное использование:
PHP:
//decl String:sItem[][] = { "rote", "[Повернуть предмет]", "remove", "[Удалить предмет]\n " };
char sItem[][] =
{
	"rote", "[Повернуть предмет]",
	"remove", "[Удалить предмет]\n "
};
StaticNumInsertItem(hMenu, 0, sItem, sizeof(sItem));


Скрины по парам, всего пять пар: 1# - первая страница, #2 - последняя.
Первая пара startNum = 0, втарая пара startNum = 1 и т.д., по нарастающей.
Между первой 1# и до последней #2, добавленные пункты в меню - статичны по выбранной позиции.

1). 0_1.JPG 0_end.JPG 2). 1-1.JPG 1_end.JPG 3). 2_1.JPG 2_end.JPG 4). 3_1.JPG 3_end.JPG 5). 4_1.JPG 4_end.JPG
 
Последнее редактирование:

Kruzya

Участник
Сообщения
12,973
Реакции
10,927
  • Команда форума
  • #3
Псевдо-генератор UUID v4
PHP:
stock void GenerateUUID(char[] szBuffer, int iBufferLength) {
  FormatEx(szBuffer, iBufferLength, "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
    // 32 bits for "time_low"
    GetRandomInt(0, 0xffff), GetRandomInt(0, 0xffff),

    // 16 bits for "time_mid"
    GetRandomInt(0, 0xffff),

    // 16 bits for "time_hi_and_version"
    // four most significant bits holds version number 4
    (GetRandomInt(0, 0x0fff) | 0x4000),

    // 16 bits, 8 bits for "clk_seq_hi_res",
    // 8 bits for "clk_seq_low",
    // two most significant bits holds zero and one for variant DCE1.1
    (GetRandomInt(0, 0x3fff) | 0x8000),

    // 48 bits for node
    GetRandomInt(0, 0xffff), GetRandomInt(0, 0xffff), GetRandomInt(0, 0xffff)
  );
}
 

_wS_

Участник
Сообщения
383
Реакции
760
Функция для получения цвета из строки.
Раньше на ExplodeString расчитывал, но только недавно понял, что оно для этого не совсем подходит.
Даже если отрезать пробелы по бокам (TrimString), то кто-то может внутри 1 лишний пробел поставить и всё сломается.

PHP:
stock bool:GetColorFromString(const String:str[], color[], const color_size)
{
    if (color_size != 3 && color_size != 4) // ток 3 или 4
        return false;
  
    decl String:s[128], i;
    strcopy(s, sizeof(s), str);
    
    if ((i = TrimString(s)) < (color_size == 4 ? 7 : 5)) // "1 3 5" или "1 3 5 7"
        return false;
    
    decl c[color_size];
    new bool:bFoundFirstNumber = false; // "xxx xxx xxx xx1" 1 было найдено
    new c_size = color_size;
  
    while (i--)
    {
        if (bFoundFirstNumber)
        {
            if (IsCharSpace(s[i]))
            {
                c[--c_size] = StringToInt(s[i]);
                bFoundFirstNumber = false;
                s[i] = 0;
              
                if (c_size == 0)
                {
                    if (TrimString(s)) // Все числа получили, но там дальше ещё есть что-то, кроме пробелов.
                        return false;
                    break;
                }
            }
            else if (IsCharNumeric(s[i]))
            {
                if (i == 0)
                {
                    c[--c_size] = StringToInt(s[i]);
                    break;
                }
            }
            else
                return false;
        }
        else if (IsCharNumeric(s[i]))
        {
            if (i == 0)
            {
                c[--c_size] = StringToInt(s[i]);
                break;
            }
            bFoundFirstNumber = true;
        }
        else if (!IsCharSpace(s[i]))
            return false;
    }
  
    if (c_size == 0 && c[0] > -1 && c[0] < 256 && c[1] > -1 && c[1] < 256 && c[2] > -1 && c[2] < 256 && (color_size == 3 || (c[3] > -1 && c[3] < 256)))
    {
        color[0] = c[0];
        color[1] = c[1];
        color[2] = c[2];
        if (color_size == 4) color[3] = c[3];
        return true;
    }
  
    return false;
}

Пример:

PHP:
decl c[4];
if (GetColorFromString("   123      215       112         151      ", c, sizeof(c)))
    PrintToServer("%d %d %d %d", c[0], c[1], c[2], c[3]);
else
    PrintToServer("Тебе даже эта функция не поможет, пойди выровняй руки.");
 

_wS_

Участник
Сообщения
383
Реакции
760
Вот так еще можно:

PHP:
new String:s[] = "        1       2        3          4     ";
new size = TrimString(s);
new bool:bNeedReplace = false;
for (new i = 0; i < size; i++)
{
    if (IsCharSpace(s[i]))
    {
        if (bNeedReplace) s[i] = '0';
        else bNeedReplace = true;
    }
    else
        bNeedReplace = false;
}

// И после этого уже ExplodeString, т.к. StringToInt("02") выдаст  2.
// Хм, так и кода меньше будет.
 
Последнее редактирование модератором:

Grey83

не пишу плагины с весны 2022
Сообщения
8,642
Реакции
5,114
Сегодня весь день делал сортировку массива для плагина вывода Топ10 по дамагу
Вот что вышло из этого:
PHP:
int iDmg[MAXPLAYERS+1],
    iKills[MAXPLAYERS+1];
...
stock void ShowDamage(int client, int places = 10)
{
    if(places < 1 || places > 10) places = 10;
    static int i, j, num, max, lst, place, dmg;
    num = dmg = lst = place = 0;
    //    оптимизация последующих циклов
    int[] clients = new int[MaxClients];
    for(i = 1, num = 0; i <= MaxClients; i++) if(IsClientInGame(i) && !IsFakeClient(i) && iDmg[i]) clients[num++] = i;
    if(!num) return;

    int[] pos    = new int[num];    // место
    int[] id    = new int[num];    // индекс

    // заполняем массив iArray
    for(i = 0; i < num && place < places;)
    {
        // переходим к следующему месту
        place++;
        // находим первого игрока на месте place по дамагу
        for(j = 0, dmg = 0; j < num; j++) if(iDmg[clients[j]] < max && iDmg[clients[j]] > dmg)
        {
            dmg    = iDmg[clients[j]];
            lst    = clients[j];
        }
        pos[i]    = place;
        id[i]    = lst;
        max = dmg;    // запоминаем максимальный урон на этом месте
        i++;

        // находим всех игроков на этом же месте
        for(j = 0; j < num && i < num; j++) if(clients[j] != lst && iDmg[clients[j]] == max)
        {
            pos[i]    = place;
            id[i]    = clients[j];
            i++;
        }
    }

    if(!client)
    {
        PrintToChatAll(" \nTop%i by damage:", places);
        for(i = 0; i < num; i++) PrintToChatAll("\x01%2i) \x04%N\x01: \x04%i\x01/\x04%i\x01dmg \x04%i\x01/\x04%i\x01kills", pos[i], id[i], iDmg[id[i]], iKills[id[i]]);
        PrintToChatAll(" \n");
        return;
    }
    PrintToChat(client, " \nTop%i by damage:", places);
    for(i = 0; i < num; i++) PrintToChat(client, "\x01%2i) \x04%N\x01: \x04%i\x01dmg \x04%i\x01kills", pos[i], id[i], iDmg[id[i]], iKills[id[i]]);
    PrintToChat(client, " \n");
}
Аналогично можно отсортировать любые массивы одинакового размера даже с разным содержимым.
Главное, чтобы под одними и теми же индексами у них хранились данные относящиеся к одним и тем же вещам (например, как в моём случае, к игрокам).
 
Последнее редактирование:

Kruzya

Участник
Сообщения
12,973
Реакции
10,927
  • Команда форума
  • #7
@Grey83, с динамическими массивами у SP есть болячка. Они не удаляются виртуальной машиной. Потому рано или поздно SM перестаёт вызывать функции плагина, потому что он видит, что стек не сходится.
Так по крайней мере на 1.8 было, и по сей день наблюдаю на билде 1.10 от июля 2018.
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,642
Реакции
5,114
@Kr1kuzya, так что, лучше создавать массив большего размера, чем такой?
 

Kruzya

Участник
Сообщения
12,973
Реакции
10,927
  • Команда форума
  • #9
@Grey83, пока не исправят - да, так лучше.
Вот когда они исправят это (я кстати хз, что им мешает, ибо разрабы точно в курсе) - тогда можно массово юзать. А пока что я б не советовал.
 

Kailo

Участник
Сообщения
194
Реакции
898
Оффтоп
PHP:
// Преобразует время в секундах в строку, пример: 3756 -> "1ч 02м 36с"
void GetStringTime(int time, char[] buffer, int maxlength)
{
    static int dims[] = {60, 60, 24, 30, 12, cellmax};
    static char sign[][] = {"с", "м", "ч", "д", "м", "г"};
    static char form[][] = {"%02i%s%s", "%02i%s %s", "%i%s %s"};
    buffer[0] = EOS;
    int i = 0, f = -1;
    bool cond = false;
    while (!cond) {
        if (f++ == 1)
            cond = true;
        do {
            Format(buffer, maxlength, form[f], time % dims[i], sign[i], buffer);
            if (time /= dims[i++], time == 0)
                return;
        } while (cond);
    }
}
 
Последнее редактирование:

Grey83

не пишу плагины с весны 2022
Сообщения
8,642
Реакции
5,114
@Kailo, вроде можно проще разбить секунды на большие промежутки времени
Вот так, например (собственно разбиение занимает всего 1 строку):
PHP:
static const char sName[][][] =
{
    {"день",    "дня",        "дней"},
    {"час",        "часа",        "часов"},
    {"минута",    "минуты",    "минут"},
    {"секунда",    "секунды",    "секунд"}
};

public void OnPluginStart()
{
    int time = GetTime() - 1471777116;
    // вот эту строку, где форматируется текст
    PrintToServer("Время, которое прошло после написания поста:%s%s%s%s", NumToName(time/3600/24, 0), NumToName(time/3600%24, 1), NumToName(time/60%60, 2), NumToName(time%60, 3));
}

stock char NumToName(int num, const int type)
{
    static char buffer[PLATFORM_MAX_PATH];
    buffer[0] = 0;

    int form;
    switch(num)
    {
        case 0:        return buffer;
        case 1:        form = 0;
        case 2,3,4:    form = 1;
        default:
        {
            if(num < 21)    form = 2;
            else switch(num%10)
            {
                case 1:        form = 0;
                case 2,3,4:    form = 1;
                default:    form = 2;
            }
        }
    }
    FormatEx(buffer, sizeof(buffer), " %i %s", num, sName[type][form]);
    return buffer;
}
[ANY] Определение окончания слова день (дня, дней)
 

Вложения

  • окончания для множественного и единственного числа.smx
    4.1 КБ · Просмотры: 31
Последнее редактирование:

Rabb1t

Амбассадор
Сообщения
2,968
Реакции
1,429
  • Команда форума
  • #12
Проверка на наличие хотя бы одного флага из строка, т.к. ReadFlagString() занесет в переменную сразу все флаги и затем в проверке через GetUserFlagBits() будет проверять наличие всех флагов из строки.
Эта же ф-я вернет true, если у игрока будет хотя бы один флаг, иначе - false.
PHP:
stock bool CheckAdminFlagsByString(int iClient, const char[] szFlagString)
{
   AdminFlag aFlag;
   int iFlags;

   for (int i = 0; i < strlen(szFlagString); i++)
   {
       if(!FindFlagByChar(szFlagString[i], aFlag))    continue;
       iFlags |= FlagToBit(aFlag);
       if (GetUserFlagBits(iClient) & iFlags)
       {
           return true;
       }
   }
   return false;
}
 
Последнее редактирование:

Rabb1t

Амбассадор
Сообщения
2,968
Реакции
1,429
  • Команда форума
  • #14
Оно ничего не проверяет
Да, чуть ошибся пока писал. xD
Если в переменную занести прочтенные флаги через эту ф-ю, то затем в проверке через GetUserFlagBits() проверять будет на наличие всех флагов из строки.
--- Добавлено позже ---
Поправил, спасибо. :)
 
Последнее редактирование:

Kruzya

Участник
Сообщения
12,973
Реакции
10,927
  • Команда форума
  • #15
Может, пригодится кому. Писалось для себя, в свободное время.
В один прекрасный вечер надоело из плагина в плагин таскать код обновления базы данных. Так родилась идея вынести это в инклуду, да ещё и максимально абстрактно (настолько, насколько это возможно).
Идея заключается в последовательном приведении БД к нужной версии (все таблицы, колонки, и так далее). Так, как обычно запускаются миграции в том же Laravel.
Саму инклуду можно взять здесь. Писалось, повторюсь, в свободное время по вечерам (и пару раз ночью). Там же пример есть простой.
Сама инклуда будет обновляться, если будет необходимо.

Вкратце о том, как использовать. Тот же пример, что и по ссылке выше, но чуть подробный, и более откомментированный.
PHP:
#include <sourcemod>
#include <dbi>
#include <dbupdater>

public void OnPluginStart()
{
    // Получаем указатель на соединение с базой данных любым удобным способом.
    //
    // Для примера, я буду получать его в один поток с игровым тиком.
    // В реальности, так делать не надо, ибо может вызвать фриз.
    char szError[256];
    Database hDb = SQL_Connect("my_database_configuration", true, szError, sizeof(szError));

    if (!hDB)
    {
        SetFailState("Database failure: %s", szError);
        return;
    }

    // Теперь инициализируем "мигратор".
    // Он на вход ничего не принимает при инициализации.
    // Для настроек, у него есть отдельные методы.
    DBUpdater_Start();

    // Настроим имя таблицы для хранения миграций (опционально).
    DBUpdater_SetTableName("mysuperplugin_migrations");

    // Настроим идентификатор плагина для хранения миграций (опционально).
    //
    // Желательно делать это: по-умолчанию, DBUpdater использует в качестве
    // идентификатора плагина - путь к нему относительно папки plugins.
    // Если плагин резко будет перемещён/переименован, DBUpdater попытается
    // ещё раз применить все миграции.
    //
    // Если плагин лежит по пути "/addons/sourcemod/plugins/test/teeest/core.smx",
    // то во внутренней таблице он получит идентификатор "test/teeest/core.smx".
    DBUpdater_SetPluginIdentifier("Core");

    // Для добавления миграций, используется метод "DBUpdater_Add()".
    //
    // На вход ему передаётся идентификатор версии (в виде числа)
    // и указатель на функцию, которая отвечает за генерацию запросов
    // для выполнения.
    //
    // Мы рекомендуем использовать следующий формат версий:
    // abbccde
    // Расшифровка: a.b.c d (alpha: 1, beta: 3, RC: 5, stable: 7, PL: 9) e
    // Например, версию 1001070 можно прочитать как "1.0.0".
    //
    // В DBUpdater входят вспомогательные макросы для добавления и
    // объявления самих функций-миграторов. Их можно выключить, объявив
    // перед подключением инклуды константу "_dbupdater_withoutmacros":
    // #define _dbupdater_withoutmacros
    // #include <dbupdater>
    //
    // В этом примере, мы не стали выключать, и покажем, как удобно их
    // использовать. Единственный макрос для добавления миграции в очередь
    // принимает только один аргумент: идентификатор версии.
    __DBUPDATER_ADD(1000070);
    __DBUPDATER_ADD(1001070);

    // Ещё можете включить "unsafe" режим. Он отличается от стандартного
    // тем, что Вы можете получать указатель на соединение с базой данных
    // посередине миграции, если не сохранили его. Делать так - НЕ
    // РЕКОМЕНДУЕТСЯ, но если очень хочется - всегда пожалуйста.
    // Но мы рекомендуем избегать использования соединения с БД в момент
    // работы "мигратора".
    // DBUpdater_MarkAsUnsafe();

    // Как только всё готово - можете запускать миграции.
    // "Раннер" на вход берёт три параметра:
    // - соединение с базой данных
    // - указатель на функцию, которая будет вызвана по окончанию работы
    // - любые кастомные данные. По-умолчанию, "0".
    // Обратите внимание: функция, которая будет вызвана по окончанию работы,
    // так же получит указатель на соединение с базой данных, но временный.
    // Если он Вам нужен - склонируйте его. Но лучше держите свой где-нибудь
    // в глобальных переменных.
    DBUpdater_Run(hDb, DBUpdater_OnFinished);

    // Всё. После этого, DBUpdater посмотрит на последнюю установленную миграцию,
    // и, если необходимо, запустит другие.
    // Если нужды нет - сразу вызовет переданную в раннер функцию.
}

// Объявляем сами миграции.
// Выше мы упоминали "удобные макросы". Один из таких - __DBUPDATER_MAKEMIGRATION().
// Он генерирует прототип функции и имя для неё, получая на вход только идентификатор
// версии.
// Например, "вызвав" его с аргументом 1000070, сгенерируется статическая функция с
// именем "_DBUpdater_1000070_Migration".
__DBUPDATER_MAKEMIGRATION(1000070)
{
    // В самой миграции нам доступны:
    // - int iInstalledVersion. Текущая установленная версия.
    // - int iProcessableVersion. Текущая обрабатываемая версия.
    // - Transaction hTxn. Транзакция, которая будет отправлена на выполнение по
    //                     завершению выполнения Вашей функции-"мигратора".
    // - DBDriver hDriver. Драйвер соединения с базой данных. Полезно, если Вы
    //                     поддерживаете несколько возможных драйверов (MySQL,
    //                     SQLite), и хотите отдавать запросы, которые точно
    //                     сработают на сервере.
    // - any data. Переданные данные при добавлении миграции. Для "макросов",
    //             это всегда "0".

    // типа создаём таблицы...
    hTxn.AddQuery(" ... ");
    hTxn.AddQuery(" ... ");
}

__DBUPDATER_MAKEMIGRATION(1001070)
{
    // В обновлении мы решили создать ещё пару таблиц, обновить старые.
    // Типа создаём...
    hTxn.AddQuery(" ... ");
    hTxn.AddQuery(" ... ");

    // Один из запросов зависит от драйвера.
    if (hDriver == view_as<DBDriver>(SQL_GetDriver("sqlite"))) // SQLite
    {
        // ...
    }
    else if (hDriver == view_as<DBDriver>(SQL_GetDriver("mysql"))) // MySQL
    {
        // ...
    }
    else if (hDriver == view_as<DBDriver>(SQL_GetDriver("pgsql"))) // PostgreSQL
    {
        // ...
    }

    // .. ещё какие-то сверхсложные запросы ..
}

// Функция, которой заканчивается исполнение всех миграций.
static void DBUpdater_OnFinished(int iInstalledVersion, Database hDB, const char[] szError, any data)
{
    // - int iInstalledVersion. Последняя успешно установленная версия.
    // - Database hDB. Соединение с базой, которое прилетело из последнего Database.Query()
    //                 ОБРАТИТЕ ВНИМАНИЕ: Этот указатель на соединение с базой умрёт по
    //                 завершению выполнения этой функции. Если он Вам нужен - склонируйте
    //                 его.
    // - char[] szError. Ошибка, если она произошла.
    // - any data. Кастомные данные, переданные в раннер.

    // ... суперсложная логика по обработке результатов, стопа плагина (если произошла ошибка) ...
}
 

rejchev

менеджер клоунов
Сообщения
1,669
Реакции
1,291
CS:GO. Немного стоков, решающие проблему с сохранением значения убийств, в обход этому посту и не только: AlliedModders - View Single Post - [CSGO update=See last post][CS:S/GO] Max/16k Money/Cash After X Rounds

PHP:
int Offset = -1, EntPlayerResource = -1;
public void OnPluginStart()
{
   EntPlayerResource = GetPlayerResourceEntity();
   Offset = FindSendPropInfo("CCSPlayerResource", "m_iKills");
}

stock int getClientFrags(int iClient, int iEntPlayerResource, int iOffset)
{
  return GetEntData(iEntPlayerResource, iOffset + iClient*4);
}

stock void setClientFrags(int iClient, int iFrags, int iEntPlayerResource, int iOffset)
{
  SetEntProp(iClient, Prop_Data, "m_iFrags", iFrags);
  SetEntData(iEntPlayerResource, iOffset + iClient*4, iFrags, 4, true);
}

stock void AddClientFrags(int iClient, int iAddFrags, int iEntPlayerResource, int iOffset)
{
  int iFrags = getClientFrags(iClient, iEntPlayerResource, iOffset);
  setClientFrags(iClient, iFrags + iAddFrags, iEntPlayerResource, iOffset);
}

stock void RemoveClientFrags(int iClient, int iReFrags, int iEntPlayerResource, int iOffset)
{
  int iFrags = getClientFrags(iClient, iEntPlayerResource, iOffset);
  setClientFrags(iClient, iFrags - iReFrags, iEntPlayerResource, iOffset);
}
 

Wend4r

I'm Source Engine masochist
Сообщения
107
Реакции
432
Функции для строки для перевода в верхний или нижний регистр. Будет полезно при работе с KeyValue, когда в древе нарушен регистр у ключей.

1-ый аргумент - входная строка, которая будет изменена.
2-ой дополнительный аргумент - с какого символа будет изменён регистр.
3-ий дополнительный аргумент - до какого символа будет изменён регистр.
Без использования дополнительных аргументов регистр будет изменён во всей строке.

Пример использования:
C#:
char sHello[] = "hello",
     sQuestion[] = "WHAT'S YOUR NAME ?";

StringToUppercase(sHello, 0, 1);
StringToLowercase(sQuestion);

PrintToServer("%s %s", sHello, sQuestion);
Вывод: Hello what's your name ?

Функции:
C#:
stock void StringToLowercase(char[] sText, int iStartLen = 0, int iEndLen = 0)
{
    int iLen = strlen(sText)+1;

    if(1 > iEndLen || iLen < iEndLen)
    {
        iEndLen = iLen;
    }

    for(int i = iStartLen; i != iEndLen; i++)
    {
        sText[i] |= 0x20;
    }
}

stock void StringToUppercase(char[] sText, int iStartLen = 0, int iEndLen = 0)
{
    int iLen = strlen(sText)+1;

    if(1 > iEndLen || iLen < iEndLen)
    {
        iEndLen = iLen;
    }

    for(int i = iStartLen; i != iEndLen; i++)
    {
        sText[i] &= ~0x20;
    }
}
 
Последнее редактирование:

DenisPukin

Капитан Костыль
Сообщения
185
Реакции
34
У меня такой вопрос:
Возможно ли изменить иконки команд ?
 

Rabb1t

Амбассадор
Сообщения
2,968
Реакции
1,429
  • Команда форума
  • #19

Grey83

не пишу плагины с весны 2022
Сообщения
8,642
Реакции
5,114
не совсем функция (т.к. тут их несколько задействовано), но можно конвертировать цвета RGB и RGBA из HEX (в виде 0xRRGGBB или 0xRGB и 0xRRGGBBAA или 0xRGBA, соответственно, как в таблицах стиля)
C++:
#pragma semicolon 1
#pragma newdecls required

public void OnPluginStart()
{
    RegConsoleCmd("sm_hex", Cmd_Hex);
}

public Action Cmd_Hex(int client, int args)
{
    if(!args)
        return Plugin_Handled;

    static char buffer[16];
    GetCmdArg(1, buffer, sizeof(buffer));
    int clr, num;
    if((num = IsColorValid(buffer)))
    {
        if(num < 5) FormatEx(buffer, sizeof(buffer), ConvertColor(buffer, num));
        StringToIntEx(buffer, clr, 16);
        if(num%4) ReplyToCommand(client, "HEX color '%s' (0x%x) is '%d %d %d'!", buffer, clr, (clr & 0xFF0000) >> 16, (clr & 0xFF00) >> 8, clr & 0xFF);
        else ReplyToCommand(client, "HEX color '%s' is (0x%x) '%d %d %d %d'!", buffer, clr, (clr & 0xFF000000) >>> 24, (clr & 0xFF0000) >> 16, (clr & 0xFF00) >> 8, clr & 0xFF);
    }
    else ReplyToCommand(client, "HEX color '%s' is invalid!", buffer);

    return Plugin_Handled;
}

stock int IsColorValid(const char[] buffer)    // проверяем валидность использованных символов
{
    int i;
    while(buffer[i])
    {
        if(!(buffer[i] >= '0' && buffer[i] <= '9')
        && !(buffer[i] >= 'A' && buffer[i] <= 'F')
        && !(buffer[i] >= 'a' && buffer[i] <= 'f'))
            return 0;
        i++;
    }
    return i == 3 || i == 4 || i == 6 || i == 8 ? i : 0;    // если количество символов равно необходимому для указания цвета, то передаём это количество, иначе отправляем 0
}

stock char ConvertColor(const char[] hex, int num)   // преобразуем цвет из короткого вида (0xRGB или 0xRGBA) в полный (0xRRGGBB или 0xRRGGBBAA)
{
    static char result[12];
    int i, j;
    for(; i <= num; i++) result[j++] = result[j++] = hex[i];
    result[j] = 0;
    return result;
}
Сообщения автоматически склеены:

думаю, в будущем в кварах буду уже использовать цвета в HEX: проще проверяется валидность параметра и удобнее хранить цвет (достаточно простого int, а не целого массива int)
 
Сверху Снизу