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

Kruzya

Участник
Сообщения
12,970
Реакции
10,914
  • Команда форума
  • #21
Я что-то подобное писал ранее для одного из модулей Дискорда. Результат здесь. Оффтоп
На вход принимает строку с, собственно, самим цветом, а возвращает int (0xRRGGBB). Поддержки альфа-канала и сокращённых записей (RGB) нет (в теории; альфу может ещё и прочитает, а сокращённую точно нет) =)
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,521
Реакции
4,980
переделываю один плагинчик
получилась готовая функция для перевода значения квара, заданного в HEX, в цвет (если данные введены некорректно, то будет просто белый непрозрачный цвет)
C-подобный:
public void OnPluginStart()
{
    ...
    cvar = CreateConVar("sm_", "00F7", "Описание", FCVAR_PRINTABLEONLY);
    cvar.AddChangeHook(CVarChanged_);
    CVarChanged_(cvar, NULL_STRING, NULL_STRING);
    ...
}

public void CVarChanged_(ConVar cvar, const char[] oldValue, const char[] newValue)
{
    iColor = HEX2RGBA(cvar);
}

stock int HEX2RGBA(ConVar cvar)    // собственно вот эта самая функция
{
    char clr[16];
    cvar.GetString(clr, sizeof(clr));
    clr[9] = 0;    // чтобы проверялось максимум 9 первых символов

    int i;
    while(clr[i])
    {
        if(!(clr[i] >= '0' && clr[i] <= '9') && !(clr[i] >= 'A' && clr[i] <= 'F') && !(clr[i] >= 'a' && clr[i] <= 'f'))
        {    // не HEX-число
            LogError("\nHEX color '%s' isn't valid!\nNew color is 0xffffffff (255 255 255 255)!\n", clr);
            return -1;
        }
        i++;
    }

    clr[8] = 0;
    if(i == 6)                                // добавляем прозрачность
    {
        clr[6] = clr[7] = 'F';
        i = 8;
    }
    else if(i == 3 || i == 4)                // короткая форма => полная форма
    {
        if(i == 3) clr[6] = clr[7] = 'F';    // добавляем прозрачность
        else clr[6] = clr[7] = clr[3];
        clr[4] = clr[5] = clr[2];
        clr[2] = clr[3] = clr[1];
        clr[1] = clr[0];
        i = 8;
    }

    if(i != 8)
    {    // невалидный цвет => 0xFFFFFFFF
        LogError("\nHEX color '%s' isn't valid!\nNew color is 0xffffffff (255 255 255 255)!\n", clr);
        return -1;
    }
    StringToIntEx(clr, i, 16);
    return i;
}

stock void DrawZoneBounds(int ct)
{
    int ct, color[4];
    color[0] = (iColor & 0xFF000000) >>> 24;
    color[1] = (iColor & 0xFF0000) >> 16;
    color[2] = (iColor & 0xFF00) >> 8;
    color[3] = iColor & 0xFF;

    float start[3], end[3];
    ...
    DrawBeam(start, end, color);
}

stock void DrawBeam(const float start[3], const float end[3], const int color[4])
{
    TE_SetupBeamPoints(start, end, g_BeamSprite, 0, 0, 0, fBuyTime, 20.0, 20.0, 0, 0.0, color, 0);
    TE_SendToAll();
}
Таким образом даже экономится место на хранение значения цвета (теперь оно занимает раз в 5 меньше места: емнип, одномерный массив занимает места сколько занимают все его ячейки + ещё одна ячейка)
 
Последнее редактирование:

JDW

Мы открываем бизнес
Сообщения
376
Реакции
325
Функция для дебага.

Макрос:
#define _DEBUG 1/0

1 - используем дебаг
0 - не используем дебаг(для релиза)

Функция:
stock void dbg(const char[] log, any ...){
    #if _DEBUG
        char buffer[1024], file[PLATFORM_MAX_PATH];

        BuildPath(Path_SM, file, PLATFORM_MAX_PATH, "logs/debug.log");

        VFormat(buffer, 1024, log, 2);
        LogToFile(file, buffer);
    #endif
}

BuildPath(Path_SM, file, PLATFORM_MAX_PATH, "logs/debug.log"); можно вынести, чтобы каждый раз не вызывать, когда мы будем использовать функцию dbg();

Пример:
dbg("Error: ", error);
 

Kruzya

Участник
Сообщения
12,970
Реакции
10,914
  • Команда форума
  • #24
Использую что-то подобное, но чутка иначе.
Самая лучшая реализация на данный момент, как мне кажется, используется в GameX. Оттуда пример и приведу:
include/GameX/Debug.inc:
#if defined _GMX_DEBUG
    char g_szDebugLog[PLATFORM_MAX_PATH];

    #define _GMX_STDDBGINIT()   _GMX_DBGINIT("GameX_Debug")
    #define _GMX_DBGINIT(%0)    BuildPath(Path_SM, g_szDebugLog, sizeof(g_szDebugLog), "logs/" ... %0 ... ".log");
    #define _GMX_DBGLOG(%0)     LogToFile(g_szDebugLog, %0);
#else
    #define _GMX_STDDBGINIT()
    #define _GMX_DBGINIT(%0)
    #define _GMX_DBGLOG(%0)
#endif

Небольшой пример использования:
GameX_ServerInformation.sp:
public void OnPluginStart()
{
    _GMX_STDDBGINIT()
    _GMX_DBGLOG("OnPluginStart()")
}

public void GameX_OnReload()
{
    _GMX_DBGLOG("GameX_OnReload()")
    Information_SendStart();
}

public void OnMapStart()
{
    _GMX_DBGLOG("OnMapStart()")
    Information_SendStart();
}

void Information_SendStart()
{
    _GMX_DBGLOG("Information_SendStart()")
    char szCurrentMap[256];
    GetCurrentMap(szCurrentMap, sizeof(szCurrentMap));

    JSONObject hRequest = new JSONObject();
    hRequest.SetString("map", szCurrentMap);
    hRequest.SetInt("max_players", Information_MaxPlayers());

    _GMX_DBGLOG("Information_SendStart(): Map '%s', MaxPlayers '%d'", szCurrentMap, Information_MaxPlayers())
    GameX_DoRequest("server/info", hRequest, Information_OnStartDelivered);
    CloseHandle(hRequest);
}

Для активации отладки, соответственно, просто добавить _GMX_DEBUG константу любым удобным способом. Я добавляю на стадии компиляции в Тревисе:
.travis.yml:
language: c
dist: bionic

env:
    - SMVERSION=1.8  COMPILER_PARAMS=""
    - SMVERSION=1.8  COMPILER_PARAMS="_GMX_DEBUG=1"
    - SMVERSION=1.9  COMPILER_PARAMS=""
    - SMVERSION=1.9  COMPILER_PARAMS="_GMX_DEBUG=1"
    - SMVERSION=1.10 COMPILER_PARAMS=""
    - SMVERSION=1.10 COMPILER_PARAMS="_GMX_DEBUG=1"

matrix:
    fast_finish: true
    allow_failures:
    - env: SMVERSION=1.8

before_install:
    - sudo apt-get update
    - sudo apt-get install gcc-multilib
    - sudo apt-get install lib32stdc++6

before_script:
    # Make directory for SourceMod
    - mkdir -p am/sm
    - cd am/sm
    - wget -q "http://www.sourcemod.net/latest.php?version=$SMVERSION&os=linux" -O sourcemod.tar.gz
    - tar -xzf sourcemod.tar.gz

    # Download git-helper script for generating defines, start him.
    - wget -q https://raw.githubusercontent.com/CrazyHackGUT/sm-plugins/master/git.sh
    - chmod +x git.sh
    - ./git.sh > addons/sourcemod/scripting/GameX.git.sp

    # Go to scripting directory, exec chmod
    - cd addons/sourcemod/scripting
    - chmod +x spcomp
    - chmod +x compile.sh

    # Copy plugins, download dependencies
    - cp -Rf ${TRAVIS_BUILD_DIR}/scripting/* ./
    - mkdir -p include/ripext
    - curl "https://raw.githubusercontent.com/ErikMinekus/sm-ripext/master/pawn/scripting/include/ripext.inc" -o include/ripext.inc
    - curl "https://raw.githubusercontent.com/ErikMinekus/sm-ripext/master/pawn/scripting/include/ripext/http.inc" -o include/ripext/http.inc
    - curl "https://raw.githubusercontent.com/ErikMinekus/sm-ripext/master/pawn/scripting/include/ripext/json.inc" -o include/ripext/json.inc
    - mkdir -p GameX

script:
    - ./spcomp $COMPILER_PARAMS GameX.git.sp GameX.sp -E -oGameX/Core
    - ./spcomp $COMPILER_PARAMS GameX.git.sp GameX_PlayerManager.sp -E -oGameX/PlayerManager
    - ./spcomp $COMPILER_PARAMS GameX.git.sp GameX_ServerInformation.sp -E -oGameX/ServerInformation

before_deploy:
    - export CUSTOM_POSTFIX=_$SMVERSION
    - if [ "$COMPILER_PARAMS" ]; then export CUSTOM_POSTFIX=$CUSTOM_POSTFIX"_debug"; fi
    - mkdir -p $TRAVIS_BUILD_DIR/../$SFTP_SOURCE_DIRECTORY/addons/sourcemod/{configs/GameX,scripting,plugins/GameX,data/gmx/cache}
    - cp -Rf $TRAVIS_BUILD_DIR/scripting/* $TRAVIS_BUILD_DIR/../$SFTP_SOURCE_DIRECTORY/addons/sourcemod/scripting/
    - cp -Rf $TRAVIS_BUILD_DIR/configs/GameX/* $TRAVIS_BUILD_DIR/../$SFTP_SOURCE_DIRECTORY/addons/sourcemod/configs/GameX/
    - cp -Rf GameX/* $TRAVIS_BUILD_DIR/../$SFTP_SOURCE_DIRECTORY/addons/sourcemod/plugins/GameX
    - cd $TRAVIS_BUILD_DIR
    - wget --user=$HTTP_USER --password=$HTTP_PASSWORD $BASE_URL/travis.sh
    - touch $TRAVIS_BUILD_DIR/../$SFTP_SOURCE_DIRECTORY/addons/sourcemod/data/gmx/cache/.keep

deploy:
    - provider: script
      skip_cleanup: true
      script: bash travis.sh
Выражение ./spcomp $COMPILER_PARAMS GameX.git.sp GameX.sp -E -oGameX/Core раскрывается в ./spcomp _GMX_DEBUG=1 GameX.git.sp GameX.sp -E -oGameX/Core, соответственно.
1569513069332.png

Размер бинаря, как можно заметить по скриншоту выше, меняется довольно мощно. Дело в том, что если отладка не включена - выражения раскрываются в ничего, и отладочный текст в следствие в бинарник не попадает.
 

Rustgame

Ленивая кошатина!
Сообщения
254
Реакции
151
Можно вопрос, как сделать разделение числа по разрядам?
Пример:1000000000 - 1.000.000.000
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,521
Реакции
4,980
Rustgame, а число в каком виде изначально: строкой или числом?
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,521
Реакции
4,980
Rustgame, вот так:
C-подобный:
static const int NUM[] = {2147483649, -1000000000, (1 << 31), 2147483647};


public void OnPluginStart()
{
    PrintToServer("\n%i = %s",    NUM[0], SplitInt(NUM[0]));
    PrintToServer("%i = %s",    NUM[1], SplitInt(NUM[1]));
    PrintToServer("%i = %s",    NUM[2], SplitInt(NUM[2]));
    PrintToServer("%i = %s\n",    NUM[3], SplitInt(NUM[3]));
}

stock char SplitInt(int number)
{
    static const int JOIN = ' ';
    static bool kkk, kk, k;
    static int i, j;
    static char buffer[16];

    buffer[0] = buffer[1] = 0;
    if(number & (1 << 31))
    {
        buffer[0] = '-';
        j = ~number;
        if(number != (1<<31)) j++;
    }
    else j = number;

    if((kkk    = (i = j/1000000000) > 0))
        Format(buffer, sizeof(buffer), "%s%i%c", buffer, i, JOIN);
    if((kk    = (i = j/1000000%1000) > 0) || kkk)
        Format(buffer, sizeof(buffer), kkk ? "%s%03i%c" : "%s%i%c", buffer, i, JOIN);
    if((k    = (i = j/1000%1000) > 0) || kk || kkk)
        Format(buffer, sizeof(buffer), kk || kkk ? "%s%03i%c" : "%s%i%c", buffer, i, JOIN);
    i = j%1000;
    if(number == (1<<31)) i++;
    Format(buffer, sizeof(buffer), k || kk || kkk ? "%s%03i%c": "%s%d%c", buffer, i, JOIN);

    return buffer;
}
Результат:
C-подобный:
-2147483647 = -2 147 483 647
-1000000000 = -1 000 000 000
-2147483648 = -2 147 483 648
2147483647 = 2 147 483 647
 

Kruzya

Участник
Сообщения
12,970
Реакции
10,914
  • Команда форума
  • #29
Отправка TE для команды по индексу.
C-подобный:
stock void TE_SendToTeam(int team_index, float delay = 0.0)
{
    int client_count = 0;
    int[] clients = new int[MaxClients];

    for (int client = MaxClients; client != 0; --client)
    {
        if (IsClientInGame(client) && GetClientTeam(client) == team_index)
        {
            clients[client_count++] = client;
        }
    }

    TE_Send(clients, client_count, delay);
}
 

RoadSide Romeo

rest in peace...
Сообщения
1,628
Реакции
1,489
@Kruzya сразу видно ньюфагов, которые не выкупают рофлы и ставят минусы.
 

RoadSide Romeo

rest in peace...
Сообщения
1,628
Реакции
1,489
@Grey83 это шутка про switch(bool) уже давно гуляет, видать ты тоже не в теме. Мне её любят припоминать, ибо один раз на этом оподливился.
 

Rabb1t

Амбассадор
Сообщения
2,968
Реакции
1,429
  • Команда форума
  • #36
Kruzya подогнал мне тут одну свою достаточно полезную сток функцию:
C-подобный:
stock bool UTIL_IsInArray(any elem, any[] elements, int size)
{
    for (int i = size-1; i != -1; --i)
    {
        if (elements[i] == elem)
        {
            return true;
        }
    }

    return false;
}

Пример использования:
C-подобный:
void func()
{
    int a = 5, b = 15, c = 25;
    if(UTIL_IsInArray(5, {a, b, c}, 3)) // 3 - потому что 3 элемента в массиве
    {
        // один из элементов прошел проверку (в данном случае a)
    }
    else
    {
        // ни один элемент не прошел проверку
    }
}

Объясняю зачем: так выглядит куда симпатичнее, нежели
C-подобный:
void func()
{
    int a = 5, b = 15, c = 25;
    if(a == 5 || b == 5 || c == 5)
    {
        // истина если a, b или c равно(ы) 5 (в нашем случае a равно 5)
    }
    else
    {
        // ложь
    }
}
 
Последнее редактирование:

Hejter

xor ebx, ebx
Сообщения
1,759
Реакции
393
Хранение значений игрока/ентити в удобном динамическом контейнере.
Значения хранятся пока игрок/ентити не будет уничтожена.

C-подобный:
    any GetEntityValue(int entity, const char[] key, any def_value = 0)
    bool GetEntityString(int entity, const char[] key, char[] value, int maxlen, char[] def_value = "")
    
    bool SetEntityValue(int entity, const char[] key, any value, bool replace = true)
    bool SetEntityString(int entity, const char[] key, const char[] value, bool replace = true)
    
    void Close(int entity = 0)


 

Kruzya

Участник
Сообщения
12,970
Реакции
10,914
  • Команда форума
  • #38
Передача прав на указатель (Handle)
Трюк основан на функциях CloneHandle() и CloseHandle(). Сначала клонируется нужный указатель, потом удаляется старый.
C-подобный:
stock void ChangeHandleOwner(Handle &hPointer, Handle hNewOwner = null) {
  Handle hDummy = CloneHandle(hPointer, hNewOwner);
  CloseHandle(hPointer);
  hPointer = hDummy;
}
 
  • Мне нравится
Реакции: NaN

Kruzya

Участник
Сообщения
12,970
Реакции
10,914
  • Команда форума
  • #39
Вызов форвардов с клоном указателя
Ещё один полезный, по-моему мнению, сток. Позволяет вызвать некий "форвард". Но в отличие от обычного форварда, передаётся на каждый плагин не оригинальный указатель, а его клон.
C-подобный:
stock void CallEventWithClonedHandle(const char[] szFunctionName, Handle hPointer) {
  Handle    hClone;
  Function  ptrFunc;
  Handle    hPlugin;
  Handle    hPlugIter = GetPluginIterator();
  Handle    hCoreHandle = GetMyHandle();

  while (MorePlugins(hPlugIter)) {
    hPlugin = ReadPlugin(hPlugIter);
    if (hPlugin == hCoreHandle || GetPluginStatus(hPlugin) != Plugin_Running)
      continue;

    ptrFunc = GetFunctionByName(hPlugin, szFunctionName);
    if (ptrFunc == INVALID_FUNCTION)
      continue;

    hClone = CloneHandle(hPointer, hPlugin);

    Call_StartFunction(hPlugin, ptrFunc);
    Call_PushCell(hClone);
    Call_Finish();
  }

  CloseHandle(hPlugIter);
}
 
  • Мне нравится
Реакции: NaN

Kruzya

Участник
Сообщения
12,970
Реакции
10,914
  • Команда форума
  • #40
Перевод IP-адреса из строкового представления в бинарное (двоичное)
Перегоняет айпишник из строкового представления - в двоичное. Позволяет сэкономить память, если он где-то впоследствии хранится, т.к. IP-адрес в бинарном представлении занимает фиксированно 4 байта.
C-подобный:
stock int IP2Bin(const char[] szAddress) {
  char szAddr[16];
  strcopy(szAddr, sizeof(szAddr), szAddress);

  int iPos = 0;
  int iResult;

  for (int iDotId = 0; iDotId < 4; ++iDotId) {
    iPos = FindCharInString(szAddr, '.', true);
    iResult |= StringToInt(szAddr[iPos+1]) << (iDotId * 8);

    if (iPos != -1)
      szAddr[iPos] = 0;
  }

  return iResult;
}
 
Сверху Снизу