Склад полезного кода [GoldSrc]

Сообщения
336
Реакции
416
Помог
7 раз(а)
mrnomore, то, что ты сказал - полная чушь. formatex никак не влияет на совместимость и его не заменяли нигде. Тут уже вопрос, что тебе нужно сделать, но предпочтение лучше всегда отдавать formatex, т.к. format является устаревшей формой, которая хранит исходную строку в буфере размером вроде 512. А еще, мне однажды сказали, что лучше не использовать add + fmt, т.к строка будет сохраняться в память.
 
Сообщения
1,083
Реакции
59
Помог
11 раз(а)
mrnomore, если на то пошло
Код:
stock Float:GetRoundRemainingTime()
    return float(get_member_game(m_iRoundTimeSecs)) - get_gametime() + Float:get_member_game(m_fRoundStartTime);
 
Сообщения
1,083
Реакции
59
Помог
11 раз(а)
Было или нет, всё же
Код:
/**
 * Установим счёт игрокам.
 *
 * @note    rh_set_score(pUser, 3, 4) - установим счёт.
 *          rh_set_score(pUser)       - обнулим.
 *
 * @param frags   - убийства.
 * @param deaths  - смерти.
 *
 * @return  nothing.
 */

#include <reapi>

stock rh_set_score(const pUser, const frags = 0, const deaths = 0) {
    set_entvar(pUser, var_frags, float(frags));
    set_member(pUser, m_iDeaths, deaths);

    message_begin(MSG_BROADCAST, MSG_SCORE);
    write_byte(pUser);
    write_short(frags);
    write_short(deaths);
    write_short(0);
    write_short(0);
    message_end();
}
8 Апр 2023
Забыл про это
Код:
#define MSG_SCORE 85
 

AUF

Сообщения
92
Реакции
8
Code_0xABC, Древняя тема)
вот под fm чисто
C++:
// Сток обнуления счёта.
stock fm_set_user_score(client, frags, deaths)
{
    const PDATA_SAFE = 2
    const m_iDeaths = 444
    const mScoreInfo = 85
    const linux_diff_player = 5

    if (pev_valid(client) != PDATA_SAFE)
        return

    set_pev(client, pev_frags, frags)
    set_pdata_int(client, m_iDeaths, deaths, linux_diff_player)

    emessage_begin(MSG_BROADCAST, mScoreInfo)
    ewrite_byte(client)
    ewrite_short(frags)
    ewrite_short(deaths)
    ewrite_short(0)
    ewrite_short(0); 0 или get_user_team(client)
    emessage_end()
}
 
Сообщения
3,593
Реакции
1,576
Помог
141 раз(а)
Исправленный для PAWN сток hex2int из этой темы.
Автор: Garey
Автор фикса: s1lent
Код:
stock hex2int(hex[], len, bool:swap=false) {
    new val = 0;
    for(new i = 0; i < len; i++)
    {
        // get current character then increment
        new byte = hex[i];
        // transform hex character to the 4bit equivalent number, using the ascii table indexes
        if (byte >= '0' && byte <= '9') byte = byte - '0';
        else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10;
        else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10;
        val = (val << 4) | (byte & 0xF);
    }
    if(swap)
    {
          val = ((val << 24) & 0xff000000) |
                ((val << 8)  & 0x00ff0000) |
                ((val >> 8)  & 0x0000ff00) |
                ((val >> 24) & 0x000000ff)
    }
    return val;
}
Diff:
stock hex2int(hex[], len, bool:swap=false) {
    new val = 0;
    for(new i = 0; i < len; i++)
    {
        // get current character then increment
        new byte = hex[i];
        // transform hex character to the 4bit equivalent number, using the ascii table indexes
        if (byte >= '0' && byte <= '9') byte = byte - '0';
        else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10;
        else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10;
        val = (val << 4) | (byte & 0xF);
    }
-   if(swap)
-   {
-       val =   ((val & 0x000000ff ) << 24 ) |
-               ((val & 0x0000ff00 ) <<  8 ) |
-               ((val & 0x00ff0000 ) >>  8 ) |
-               ((val & 0xff000000 ) >> 24 )
-   }
+   if(swap)
+   {
+       val =   ((val << 24) & 0xff000000) |
+               ((val << 8)  & 0x00ff0000) |
+               ((val >> 8)  & 0x0000ff00) |
+               ((val >> 24) & 0x000000ff)
+   }
    return val;
}
Код Garey правильный, но так как в PAWN нет такого понятия как unsigned, чтобы хеши правильно переворачивались надо использовать signed реализацию

photo_2023-04-23_12-54-16.jpg
 
Сообщения
3,593
Реакции
1,576
Помог
141 раз(а)
Стоки для удобного предварительного кеширования файлов.

У всех бывают ситуации, когда при установке новых плагинов на сервер, вы забываете загрузить один или несколько ресурсов к нему. Особенно не приятно, если забытой оказывается модель - тогда сервер просто крашится.
Данные функции позволяют избежать этой ситуации, проверяя наличие файлов на сервере в момент их прекеша. В случае с моделью, плагин будет автоматически остановлен, если она отсутствует.
В остальных случаях, у вас есть возможность не останавливать плагин в случае отсутствия, например, звука. Тогда плагин предупредит вас в консоли сервера о том, какой файл отсутствует.
Код:
/**
* Precaches a model file.
*
* @note Can only be used inside of the plugin_precache() forward.
* @note The plugin will automatically stop if file not found, to prevent the server from crashing.
*
* @param fileName  Path to the model file
*
* @return          Unique cache id of the model or 0 if file not found
* @error           If called outside of the plugin_precache() forward, an error is
*                  thrown.
*/
stock precache_model_ex(const fileName[]) {
    if (file_exists(fileName, true)) {
        return precache_model(fileName);
    }

    set_fail_state("Model <%s> not found. The plugin has been stopped.", fileName);
    return 0;
}

/**
* Precaches a sound file.
*
* @note Can only be used inside of the plugin_precache() forward.
* @note The filepath is always relative to the "sound" folder, and the file has
*       to be a wav file. Precaching a file with this will add it to the engine
*       sound table, making it available for usage in emit_sound() for example.
* @note Precaching other filetypes (such as mp3 music), optionally in different
*       locations, has to be done with precache_generic()
* @note Warns you if a file is not found
*
* @param fileName      Path to the sound file
* @param stopPlugin    If true, the plugin will be stopped if there is no file
*
* @return              Unique cache id of the sound or 0 if file not found
* @error               If called outside of the plugin_precache() forward, an error is
*                      thrown.
*/
stock precache_sound_ex(const fileName[], bool:stopPlugin = false) {
    if (file_exists(fileName, true)) {
        return precache_sound(fileName);
    }

    if (stopPlugin) {
        set_fail_state("Sound <%s> not found. The plugin has been stopped.", fileName);
    } else {
        log_amx("Sound <%s> not found.", fileName);
    }

    return 0;
}

/**
* Precaches a generic file.
*
* @note Can only be used inside of the plugin_precache() forward.
* @note Precaching sounds with this will not add them to the engine sound table.
* @note Warns you if a file is not found
*
* @param fileName      Path to the file
* @param stopPlugin    If true, the plugin will be stopped if there is no file
*
* @return              Unique cache id of the file or 0 if file not found
* @error               If called outside of the plugin_precache() forward, an error
*                      is thrown.
*/
stock precache_generic_ex(const fileName[], bool:stopPlugin = false) {
    if (file_exists(fileName, true)) {
        return precache_generic(fileName);
    }

    if (stopPlugin) {
        set_fail_state("Generic file <%s> not found. The plugin has been stopped.", fileName);
    } else {
        log_amx("Generic file <%s> not found.", fileName);
    }

    return 0;
}
 
Сообщения
3,593
Реакции
1,576
Помог
141 раз(а)
Небольшой фикс для precache_sound_ex из поста выше. Забыл, что для прекеша звука указывается путь не относительно папки мода, а относительно папки sound, поэтому file_exists не мог обнаружить файл.
Код:
stock precache_sound_ex(const fileName[], bool:stopPlugin = false) {
    if (file_exists(fmt("sound/%s", fileName), true)) {
        return precache_sound(fileName);
    }

    if (stopPlugin) {
        set_fail_state("Sound <%s> not found. The plugin has been stopped.", fileName);
    } else {
        log_amx("Sound <%s> not found.", fileName);
    }

    return 0;
}
 
Сообщения
1,032
Реакции
828
Помог
10 раз(а)
Сток для сканирования директории и получения списка файлов по необходимым критериям.

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

Код:
stock Array:scandir(
    const directory[],
    const starts_with[] = "",
    const ends_with[] = "",
    const contains[] = "",
    const bool:ignorecase = false,
    const SortMethod:sort_method = Sort_Ascending,
    const FileType:file_type = FileType_Unknown,
    const bool:use_valve_fs = false,
    const valve_path_id[] = "GAME"
) {
    if (!dir_exists(directory, use_valve_fs)) {
        /* Can't find the directory */
        return any:-2;
    }

    new pos;
    new dirh;
    new endPos;
    new startPos;
    new file[64];
    new Array:array;
    new FileType:fileType;

    dirh = open_dir(fmt("%s", directory), file, charsmax(file), fileType, use_valve_fs, valve_path_id);

    if (dirh == 0) {
        /* Can't open the directory */
        return any:-1;
    }

    array = ArrayCreate(64);

    while (next_file(dirh, file, charsmax(file), fileType) == 1) {
        if (file[0] == '.') {
            continue;
        }

        if (file_type != FileType_Unknown && fileType != file_type) {
            continue;
        }

        if (starts_with[0] != EOS) {
            pos = startPos = strlen(starts_with);

            if (strncmp(file, starts_with, pos, ignorecase) != 0) {
                continue;
            }
        }

        if (ends_with[0] != EOS) {
            pos = endPos = strlen(file) - strlen(ends_with);

            if (pos < 0 || strcmp(file[pos], ends_with, ignorecase) != 0) {
                continue;
            }
        }

        if (contains[0] != EOS) {
            pos = strfind(file, contains, ignorecase, startPos);

            if (pos == -1 || endPos > 0 && pos > endPos) {
                continue;
            }
        }

        ArrayPushString(array, file);
    }

    close_dir(dirh);

    if (ArraySize(array) == 0) {
        /* Can't find the files */
        return Invalid_Array;
    }

    if (sort_method != Sort_Ascending) {
        SortADTArray(array, sort_method, Sort_String);
    }

    return array;
}
Для примера:
Код:
public plugin_init() {
    new Array:listFiles = scandir("maps", "cs_", ".bsp", "47");
   
    if (listFiles <= any:0) {
        return;
    }

    new size = ArraySize(listFiles);

    for (new i = 0; i < size; ++i) {
        console_print(0, "%a", ArrayGetStringHandle(listFiles, i));
    }
}
Сканирование директории на основе поиска wildcard_match предложенным SergeyShorokhov
Код:
stock Array:scandir2(
    const directory[],
    const contains[] = "",
    const SortMethod:sort_method = Sort_Ascending,
    const FileType:file_type = FileType_Unknown,
    const bool:use_valve_fs = false,
    const valve_path_id[] = "GAME"
) {
    if (!dir_exists(directory, use_valve_fs)) {
        /* Can't find the directory */
        return any:-2;
    }

    new dirh;
    new file[64];
    new Array:array;
    new FileType:fileType;

    dirh = open_dir(fmt("%s", directory), file, charsmax(file), fileType, use_valve_fs, valve_path_id);

    if (dirh == 0) {
        /* Can't open the directory */
        return any:-1;
    }

    array = ArrayCreate(64);

    while (next_file(dirh, file, charsmax(file), fileType) == 1) {
        if (file[0] == '.') {
            continue;
        }

        if (file_type != FileType_Unknown && fileType != file_type) {
            continue;
        }

        if (!wildcard_match(file, contains)) {
            continue;
        }

        ArrayPushString(array, file);
    }

    close_dir(dirh);

    if (ArraySize(array) == 0) {
        /* Can't find the files */
        return Invalid_Array;
    }

    if (sort_method != Sort_Ascending) {
        SortADTArray(array, sort_method, Sort_String);
    }

    return array;
}

stock bool:wildcard_match(const text[], const pattern[]) {
    new n = strlen(text);
    new m = strlen(pattern);
    new i = 0, j = 0, startIndex = -1, match = 0;

    while (i < n) {
        if (j < m && (pattern[j] == '?' || pattern[j] == text[i])) {
            i++;
            j++;
        } else if (j < m && pattern[j] == '*') {
            startIndex = j;
            match = i;
            j++;
        } else if (startIndex != -1) {
            j = startIndex + 1;
            match++;
            i = match;
        } else {
            return false;
        }
    }

    while (j < m && pattern[j] == '*') {
        j++;
    }

    return j == m;
}
Для примера:
Код:
public plugin_init() {
    new Array:listFiles2 = scandir2("maps", "cs_*47*.bsp");

    if (listFiles2 <= any:0) {
        return;
    }

    new size = ArraySize(listFiles2);

    for (new i = 0; i < size; ++i) {
        console_print(0, "%a", ArrayGetStringHandle(listFiles2, i));
    }
}
 
Последнее редактирование:
Сообщения
2,751
Реакции
3,017
Помог
61 раз(а)
получения списка файлов по необходимым критериям
You case:
C:
test() {
    new str[] = "maps\cs_747.bsp"
    new patterns_test[][] = {
        "maps?cs_*47*.bsp",
        "maps*.bsp",
        "maps*de_*.bsp"
    }

    for (new i; i < sizeof patterns_test; i++) {
        new res = wildcard_match(str, patterns_test[i])
        server_print("`%s` match -> `%s` == %s",
            str, patterns_test[i],
            res ? "true" : "false"
        )
    }

    /*
        `maps\cs_747.bsp` match -> `maps?cs_*47*.bsp` == true
        `maps\cs_747.bsp` match -> `maps*.bsp` == true
        `maps\cs_747.bsp` match -> `maps*de_*.bsp` == false
     */
}

Wildcard match from https://www.geeksforgeeks.org/wildcard-pattern-matching/


Time Complexity: O(n), where n is the length of the given text.

Auxiliary Space: O(1), because we only use constant amount of extra memory to store just the two pointers.
C:
#include <amxmodx>

public plugin_init() {
    test()
}

stock bool: wildcard_match(const text[], const pattern[]) {
    new n = strlen(text)
    new m = strlen(pattern)
    new i = 0, j = 0, startIndex = -1, match = 0

    while (i < n) {
        if (j < m && (pattern[j] == '?' || pattern[j] == text[i])) {
            i++
            j++
        } else if (j < m && pattern[j] == '*') {
            startIndex = j
            match = i
            j++
        } else if (startIndex != -1) {
            j = startIndex + 1
            match++
            i = match
        } else {
            return false
        }
    }

    while (j < m && pattern[j] == '*') {
        j++
    }

    return j == m
}

test() {
    new str[] = "baaabab"
    new patterns_test[][] = {
        "*****ba*****ab",
        "ba*****ab",
        "ba*ab",
        "a*ab",
        "a*****ab",
        "*a*****ab",
        "ba*ab****",
        "****",
        "*",
        "aa?ab",
        "b*b",
        "a*a",
        "baaabab",
        "?baaabab",
        "*baaaba*"
    }

    for (new i; i < sizeof patterns_test; i++) {
        new res = wildcard_match(str, patterns_test[i])
        server_print("`%s` match -> `%s` == %s",
            str, patterns_test[i],
            res ? "true" : "false"
        )
    }

    /*
        `baaabab` match -> `*****ba*****ab` == true
        `baaabab` match -> `ba*****ab` == true
        `baaabab` match -> `ba*ab` == true
        `baaabab` match -> `a*ab` == false
        `baaabab` match -> `a*****ab` == false
        `baaabab` match -> `*a*****ab` == true
        `baaabab` match -> `ba*ab****` == true
        `baaabab` match -> `****` == true
        `baaabab` match -> `*` == true
        `baaabab` match -> `aa?ab` == false
        `baaabab` match -> `b*b` == true
        `baaabab` match -> `a*a` == false
        `baaabab` match -> `baaabab` == true
        `baaabab` match -> `?baaabab` == false
        `baaabab` match -> `*baaaba*` == true
    */
}
 
Последнее редактирование:
Сообщения
459
Реакции
272
Помог
9 раз(а)
порт utf8codepoint из C

Код:
utf8codepoint(const str[], index, &out_codepoint) {
  if ((0xf8 & str[index]) == 0xf0) {
    out_codepoint = ((0x07 & str[index]) << 18) | ((0x3f & str[index + 1]) << 12) | ((0x3f & str[index + 2]) << 6) | (0x3f & str[index + 3])
    return index + 4
  } else if ((0xf0 & str[index]) == 0xe0) {
    out_codepoint = ((0x0f & str[index]) << 12) | ((0x3f & str[index + 1]) << 6) | (0x3f & str[index + 2])
    return index + 3
  } else if ((0xe0 & str[index]) == 0xc0) {
    out_codepoint = ((0x1f & str[index]) << 6) | (0x3f & str[index + 1])
    return index + 2
  } else {
    out_codepoint = str[index]
    return index + 1
  }
}
Код:
#include <amxmodx>

public plugin_init() {
  new str[] = "Пример строки"
  new index, codepoint

  while (str[index] != EOS) {
    index = utf8codepoint(str, index, codepoint)
    server_print("%d", codepoint)
  }

  new str2[] = "Пример строки 2";
  server_print("Строки%s равны", utf8stringcompare(str, str2) ? "" : " не");
}

utf8stringcompare(str1[], str2[]) {
  new codepoint1, codepoint2;
  new index1 = 0, index2 = 0;

  while (str1[index1] != EOS && str2[index2] != EOS) {
    index1 = utf8codepoint(str1, index1, codepoint1)
    index2 = utf8codepoint(str2, index2, codepoint2)

    if (codepoint1 != codepoint2)
      return 0

    if (str1[index1] == EOS && str2[index2] == EOS)
      return 1
  }

  return 0
}

utf8codepoint(str[], index, &out_codepoint) {
  if ((0xf8 & str[index]) == 0xf0) {
    out_codepoint = ((0x07 & str[index]) << 18) | ((0x3f & str[index + 1]) << 12) | ((0x3f & str[index + 2]) << 6) | (0x3f & str[index + 3])
    return index + 4
  } else if ((0xf0 & str[index]) == 0xe0) {
    out_codepoint = ((0x0f & str[index]) << 12) | ((0x3f & str[index + 1]) << 6) | (0x3f & str[index + 2])
    return index + 3
  } else if ((0xe0 & str[index]) == 0xc0) {
    out_codepoint = ((0x1f & str[index]) << 6) | (0x3f & str[index + 1])
    return index + 2
  } else {
    out_codepoint = str[index]
    return index + 1
  }
}

От the_hunter
Макрос, чтобы определить, является ли символ ASCII
#define is_ascii(%0) (%0 & (~0x7F)) == 0
 
Последнее редактирование:
Сообщения
336
Реакции
416
Помог
7 раз(а)
Функция для разбиения, как положительных так и отрицательных чисел на разряды тысячных.

Код:
stock DivideByGroups(const szString[], szOutStr[], iLen) {
    new szDest[32];
    new iDestPos, iNegative = szString[0] == '-' ? 1 : 0, iNumLen = strlen(szString) - iNegative;
    new iMultiplicity = iNumLen / 3, iRemainder = iNumLen - iMultiplicity * 3;
    iMultiplicity = iRemainder == 0 ? (iMultiplicity - 1) : iMultiplicity;

    if(iNegative) {
        szDest[iDestPos++] = '-';
        iNumLen++;
    }

    for(new i = iNegative; i < iNumLen; i++) {
        if(i == iNumLen - iMultiplicity * 3) {
            szDest[iDestPos++] = ' ';
            iMultiplicity--;
        }

        szDest[iDestPos++] = szString[i];
    }

    copy(szOutStr, iLen, szDest);
}
Код:
#include <amxmodx>

public plugin_init() {
    new const szNumbers[][] = {
        "-1000",
        "-10000",
        "-100000",
        "-1000000",
        "-10000000",
        "-100000000",
        "-1000000000",
        "-10000000000",
        "-100000000000",
        "-1000000000000",
        "1000000000000",
        "100000000000",
        "10000000000",
        "1000000000",
        "100000000",
        "10000000",
        "1000000",
        "100000",
        "10000",
        "1000"
    };

    new szDest[32];

    for(new i; i < sizeof(szNumbers); i++) {
        DivideByGroups(szNumbers[i], szDest, charsmax(szDest));
        server_print("%s: %s", szNumbers[i], szDest);
    }
}

stock DivideByGroups(const szString[], szOutStr[], iLen) {
    new szDest[32];
    new iDestPos, iNegative = szString[0] == '-' ? 1 : 0, iNumLen = strlen(szString) - iNegative;
    new iMultiplicity = iNumLen / 3, iRemainder = iNumLen - iMultiplicity * 3;
    iMultiplicity = iRemainder == 0 ? (iMultiplicity - 1) : iMultiplicity;

    if(iNegative) {
        szDest[iDestPos++] = '-';
        iNumLen++;
    }

    for(new i = iNegative; i < iNumLen; i++) {
        if(i == iNumLen - iMultiplicity * 3) {
            szDest[iDestPos++] = ' ';
            iMultiplicity--;
        }

        szDest[iDestPos++] = szString[i];
    }

    copy(szOutStr, iLen, szDest);
}
1686500315312.png
11 Июн 2023
Функция для получения разрядов числа.

Код:
stock DivideByGroups2(iValue) {
    new iCnt, iCur;

    while(iValue) {
        iCur = iValue % 10;
        iValue /= 10;

        for(new i; i < iCnt; i++)
            iCur *= 10;

        iCnt++;
    }
}
Код:
#include <amxmodx>

public plugin_init() {
    DivideByGroups2(61246127);
}

stock DivideByGroups2(iValue) {
    new iCnt, iCur;
    server_print("iValue: %d", iValue);

    while(iValue) {
        iCur = iValue % 10;
        iValue /= 10;

        for(new i; i < iCnt; i++)
            iCur *= 10;

        iCnt++;

        server_print("iCur: %d", iCur);
    }
}
1686500574286.png
 
Последнее редактирование:
Сообщения
94
Реакции
59
Помог
4 раз(а)
SergeyShorokhov, Привет

Проверка находится ли игрок в ослеплении, для ReAPI.

Код:
stock bool: IsBlind(pPlayer) {
return bool:(Float: get_member(pPlayer, m_blindStartTime) + Float: get_member(pPlayer, m_blindFadeTime) >= get_gametime());
}
Этот код работает, но не до конца.
Крч, если игрок ослеп, все нормально.
А вот если ты наблюдатель, игрок, за которым ты не смотришь, ослеп, а после ты переключил на него камеру, код выдает False
Как бы физически ты слепой, но код этого не видит, потому что ты не наблюдал за ним, когда он ослеп.

Глову ломаю и не знаю как отловить такой момент :(
 
Сообщения
336
Реакции
416
Помог
7 раз(а)
WessTorn,
Код:
stock bool:IsBlind(const iPlayer) {
    return bool:(Float:get_member(iPlayer, m_blindUntilTime) > get_gametime());
}
 
Сообщения
459
Реакции
272
Помог
9 раз(а)
WessTorn, нужно в параметр pPlayer передавать именно идентификатор игрока за которым следишь, а не свой
Должно работать
 
Сообщения
94
Реакции
59
Помог
4 раз(а)
ufame, Мне нужно у наблюдателя убрать эффект флеша.
1 раз, а не весь синк
 
Сообщения
94
Реакции
59
Помог
4 раз(а)
Алексеич, Понял, извините. Не праильно понял описание темы "Обсуждаем"
 
Сообщения
3,056
Реакции
1,739
Помог
80 раз(а)
WessTorn, я думаю, что обсудить можно, если ты разбираешься в коде ) а сообщения типа "у меня не работает" я думаю лучше решать в разделе "проблема с работой плагина" или "вопросы по модификации"
 
Сообщения
58
Реакции
49
Помог
2 раз(а)
Писал ради интереса аналог Crontab с помощью ChatGPT чтобы узнать на что он способен. На полноценный плагин это не тянет т.к. не полностью учтен синтаксис к примеру он не поймет " 1,2-3,5/10 * * * * " такую задачу, но понимает простые к примеру " * 1,2 5/10 3-4 * ".
Код:
#include <amxmodx>
#include <amxmisc>
#define FILE_CRON "crontab.ini"

public plugin_init()
{
    register_plugin("CronTab Lite", "0.1", "uMk0");
}
enum _:settings
{
    G_CRON[64],
    G_TASK[512]
}
new gSetting[1024][settings];

public plugin_precache()
{
    new szLine[1024], szPath[64], szPathFile[128], i = 0;
    get_localinfo("amxx_configsdir", szPath, charsmax(szPath));
    formatex(szPathFile, charsmax(szPathFile), "%s/%s", szPath, FILE_CRON);
    new fp = fopen(szPathFile, "rt");
    if (fp)
    {
        while (!feof(fp))
        {
            fgets(fp, szLine, sizeof(szLine));
            trim(szLine);
            if (!szLine[0] || szLine[0] == ';')
                continue;

            parse(szLine, gSetting[i][G_CRON], charsmax(gSetting[][G_CRON]), gSetting[i][G_TASK], charsmax(gSetting[][G_TASK]));

            i++;
        }
        fclose(fp);
    }
    new szCurrentSec[32];
    get_time("%S", szCurrentSec, 31);
    new Float:fCurrentSec = str_to_float(szCurrentSec);
    set_task((60.0 - fCurrentSec), "globalMinuteStart", 0, _, _, "a", 1);
}

public globalMinuteStart()
{
    Task_Execute()
    set_task(60.0, "Task_Execute", 0, _, _, "b");
}

public Task_Execute()
{
    for (new i = 0; i < sizeof(gSetting); i++)
    {
        if (!gSetting[i][G_CRON]) break;
        if (Cron_Matches(gSetting[i][G_CRON]))
        {
            server_cmd(gSetting[i][G_TASK]);
        }
    }
}

public Cron_Matches(szCron[])
{
    new szMinute[8], szHour[8], szDay[8], szMonth[8], szWeekday[8], szTmp[64];
    strtok(szCron, szMinute, charsmax(szMinute), szTmp, charsmax(szTmp), _, 1);
    strtok(szTmp, szHour, charsmax(szHour), szTmp, charsmax(szTmp), _, 1);
    strtok(szTmp, szDay, charsmax(szDay), szTmp, charsmax(szTmp), _, 1);
    strtok(szTmp, szMonth, charsmax(szMonth), szTmp, charsmax(szTmp), _, 1);
    strtok(szTmp, szWeekday, charsmax(szWeekday), szTmp, charsmax(szTmp), _, 1);

    new szCurrentMinute[8], szCurrentHour[8], szCurrentDay[8], szCurrentMonth[8], szCurrentWeekday[8];
    get_time("%M", szCurrentMinute, charsmax(szCurrentMinute));
    get_time("%H", szCurrentHour, charsmax(szCurrentHour));
    get_time("%d", szCurrentDay, charsmax(szCurrentDay));
    get_time("%m", szCurrentMonth, charsmax(szCurrentMonth));
    get_time("%w", szCurrentWeekday, charsmax(szCurrentWeekday));
    new iCurrentMinute    = str_to_num(szCurrentMinute);
    new iCurrentHour    = str_to_num(szCurrentHour);
    new iCurrentDay        = str_to_num(szCurrentDay);
    new iCurrentMonth    = str_to_num(szCurrentMonth);
    new iCurrentWeekday = str_to_num(szCurrentWeekday);

    if (!Cron_Field_Matches(szMinute, iCurrentMinute, "0-59") || !Cron_Field_Matches(szHour, iCurrentHour, "0-23") || !Cron_Field_Matches(szDay, iCurrentDay, "1-31") || !Cron_Field_Matches(szMonth, iCurrentMonth, "1-12") || !Cron_Field_Matches(szWeekday, iCurrentWeekday, "0-6"))
    {
        return false;
    }
    return true;
}

public Cron_Field_Matches(szField[], iValue, szRange[])
{
    new szRangeStart[8], szRangeEnd[8], szStep[8], szArg1[10];
    strtok(szRange, szRangeStart, charsmax(szRangeStart), szRangeEnd, charsmax(szRangeEnd), '-');
    new iStart = str_to_num(szRangeStart);
    new iEnd   = str_to_num(szRangeEnd);
    if (contain(szField, "/") != -1)
    {
        strtok(szField, szArg1, charsmax(szArg1), szStep, charsmax(szStep), '/');
        new iStep = str_to_num(szStep);
        if (iStart < iStep || iStep > iEnd)
            return false;
        if (iStep == 0)
            return false;
        if (iValue % iStep != 0)
            return false;

        return true;
    }
    if (contain(szField, ",") != -1)
    {
        new szValue[8];
        while (strtok(szField, szValue, charsmax(szValue), szArg1, charsmax(szArg1), ','))
        {
            new iStep = str_to_num(szValue);
            if (!(iStart < iStep || iStep > iEnd))
            {
                if (iStep == iValue)
                    return true;
            }
        }
        return false;
    }
    if (contain(szField, "-") != -1)
    {
        new szFieldRangeStart[8], szFieldRangeEnd[8];
        strtok(szField, szFieldRangeStart, charsmax(szFieldRangeStart), szFieldRangeEnd, charsmax(szFieldRangeEnd), '-');
        new iFieldStart = str_to_num(szFieldRangeStart);
        new iFieldEnd    = str_to_num(szFieldRangeEnd);
        if (iStart < iFieldStart || iFieldStart > iEnd)
            return false;
        if (iStart < iFieldEnd || iFieldEnd > iEnd)
            return false;
        if (iValue < iFieldStart || iValue > iFieldEnd)
            return false;
        return true
    }
    if (contain(szField, "*") != -1)
    {
        return true
    }
    new iStep = str_to_num(szField);
    if (!(iStart < iStep || iStep > iEnd))
    {
        if (iStep == iValue)
            return true;
    }
    return false;
}
INI:
"* * * * *" "say Message every minute"
"10/1 * * * *" "say Message every 10th minute from 1 through 59."
"30-40 * * * *" "say Message every minute from 30 through 40."
"5,10,30,40 * * * *" "say Message minute 5, 10, 30, and 40."
 

Пользователи, просматривающие эту тему

Сейчас на форуме нет ни одного пользователя.
Сверху Снизу