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

wopox1337

Разработчик
Скриптер
Проверенный
Сообщения
30
Реакции
31
Баллы
18
Обсуждаем, выкладываем полезные стоки для скриптера. Делимся решением задач.

Оглавление:
  • todo....
 
Данный сток предназначен для для проверки наличия физических препятствий (в основном стен) между двумя точками. Метод выполняет трассировку луча между заданными координатами, игнорируя стекло, монстров, игроков и снаряды, и возвращает true, если луч столкнулся с чем-то на пути, и false, если путь свободен.

Код:
stock is_wall_between_points(Float: start[3], Float: end[3], ignore_ent) {
    // Create the trace handle! It is best to create it!
    new ptr = create_tr2()

    // The main traceline function!
    // This function ignores GLASS, MISSILE and MONSTERS!
    // Here is an example of how you should combine all the flags!
    engfunc(EngFunc_TraceLine, start, end, IGNORE_GLASS | IGNORE_MONSTERS | IGNORE_MISSILE, ignore_ent, ptr)

    // We are interested in the fraction parameter
    new fraction
    get_tr2(ptr, TR_flFraction, fraction)

    // Free the trace handle (don't forget to do this!)
    free_tr2(ptr)

    // If = 1.0 then it didn't hit anything!
    return (fraction != 1.0)
}
 
Стоки с прошлого сайта. Возможно, немного модифицированные.
Код:
/**
 * Парсинг строки для получения времени в секундах
 *
 * @param szTime    Строка для парсинга
 *
 * @return          Время в секундах
 */
stock parseTime(const szTime[])
{
    const SecondsInMinute = 60;
    const SecondsInHour = 3600;
    const SecondsInDay = 86400;
    const SecondsInWeek = 604800;
    const SecondsInMonth = 25920000;
    const SecondsInYear = 31536000;

    new k, t, h, bool:check_chars;

    for (new i; szTime[i] != EOS; i++)
    {
        if (isspace(szTime[i]))
        {
            check_chars = true;
            continue;
        }

        switch(szTime[i])
        {
            case '0'..'9':
            {
                if (check_chars)
                {
                    check_chars = false;
                    k += t;
                }

                t = (t * 10) + (szTime[i] - '0');
            }

            case 's', 'S':
            {
                k += t;
                t = 0;
            }

            case 'i','I':
            {
                k += t * SecondsInMinute;
                t = 0;
            }

            case 'h','H':
            {
                k += t * SecondsInHour;
                t = 0;
            }

            case 'd','D':
            {
                k += t * SecondsInDay;
                t = 0;
            }

            case 'w','W':
            {
                k += t * SecondsInWeek;
                t = 0;
            }

            case 'm','M':
            {
                k += t * SecondsInMonth;
                t = 0;
            }

            case 'y','Y':
            {
                k += t * SecondsInYear;
                t = 0;
            }

            default:
            {
                break;
            }
        }
    }

    return k + t;
}

/**
 * Замена для SQL запроса
 *
 * @param szDest   Строка
 * @param iLen     Длина строки
 *
 * @noreturn
 */
stock UTIL_SqlEscapeString(szDest, const iLen)
{
    replace_all(szDest, iLen, "\\", "\\\\");
    replace_all(szDest, iLen, "\0", "\\0");
    replace_all(szDest, iLen, "\n", "\\n");
    replace_all(szDest, iLen, "\r", "\\r");
    replace_all(szDest, iLen, "\x1a", "\Z");
    replace_all(szDest, iLen, "'", "\'");
    replace_all(szDest, iLen, "^"", "\^"");
}
 
Код:
/**
 * Получает размеры хитбоксов у модели (динамически)
 *
 * @param szModel  Путь до модели (Пр. "models/bag.mdl")
 * @param flMaxs   Local BB max.
 * @param flMins   Local BB min.
 * @param flSize   max - min
 *
 * @return         true/false
 */

stock bool:UTIL_GetModelHitBox(const szModel[], Float: flMaxs[3], Float: flMins[3], Float: flSize[3])
{
    new hFile = fopen(szModel, "rb");

    if (!hFile)
    {
        server_print("Can`t open %s", szModel);
        return false;
    }

    fseek(hFile, 160, SEEK_SET);

    new iBlock;
    fread(hFile, iBlock, BLOCK_INT);
    fseek(hFile, iBlock + 8, SEEK_SET);
    
    new Float: flVec[6];
    fread_blocks(hFile, _:flVec, 6, BLOCK_INT);
    fclose(hFile);

    flMins[0] = flVec[0];
    flMins[1] = flVec[1];
    flMins[2] = flVec[2];
    flMaxs[0] = flVec[3];
    flMaxs[1] = flVec[4];
    flMaxs[2] = flVec[5];
    flSize[0] = flVec[3] - flVec[0];
    flSize[1] = flVec[4] - flVec[1];
    flSize[2] = flVec[5] - flVec[2];

    return true;
}
 
stock UTIL_SqlEscapeString(szDest, const iLen)
Неправильный сток, поскольку намешаны CTRLCHAR "\" и "^", должно быть, для "^" (по-умолчанию):
Код:
#define _SC(%0)                     %0, charsmax(%0)

replace_all(_SC(s_tmp), "^r", "");
replace_all(_SC(s_tmp), "^n", "");
replace_all(_SC(s_tmp), "^t", "");
replace_all(_SC(s_tmp), "^x1a", "");
replace_all(_SC(s_tmp), "\", "\\");
replace_all(_SC(s_tmp), "`", "\`");
replace_all(_SC(s_tmp), "^'", "\^'");
replace_all(_SC(s_tmp), "^"", "\^"");

Но 8 раз проходиться по всем символам исходной строки, с вызовом натива, на мой взгляд, глупое решение. Я бы сделал, с учетом специфики ее использования, как-то так:

Код:
stock UTIL_SqlEscapeString(const str[]) {
 
    new i, j, result[ACS_MAX_BUFFER_SIZE];

    for (; str[i] && j < charsmax(result); i++) {
        switch (str[i]) {
            case '^r', '^n', '^t', '^x1a':
                continue;
            case '`', '^'', '^"', '\': {
                result[j++] = '\';
                if (j >= charsmax(result))
                    break;
            }
        }
        result[j++] = str[i];
    }

    return result;
}
 
Последнее редактирование:
Потрясающие названия переменных. Когда уже забудете про венгерскую нотацию?

Возвращать из функции массив не очень хорошая идея. Во первых, будет лишнее копирование массива из стэка. Во вторых, размер массива ограничивается в функции. В третьих, всегда создается новый массив, даже когда он не нужен.
 
Потрясающие названия переменных. Когда уже забудете про венгерскую нотацию?

Возвращать из функции массив не очень хорошая идея. Во первых, будет лишнее копирование массива из стэка. Во вторых, размер массива ограничивается в функции. В третьих, всегда создается новый массив, даже когда он не нужен.

Это пример, можно перевести на любую нотацию. Копирование будет в любом случае, из стека или из буфера в оригинальную строку, если это делать также безопасно. Ограничение на размер массива задается в #define инклуда ваших проектов, обычно буфер 256 ячеек.

Идея состоит в том, чтобы использовать это в одной строке:
Код:
formatex(_SC(s_query), "SELECT * FROM `player_names` WHERE name = '%s'", _qq(s_name));

На мой непрофессиональный взгляд это просто удобнее и быстрее. У меня этот натив "в 3 строки кода", вообще в модуле.
 
Последнее редактирование:
Это пример, можно перевести на любую нотацию.
Я не просто так придираюсь. Этот топик для примеров кода, верно? Логично ведь тогда подавать хорошие примеры.
А хорошие примеры не могут быть с именами вроде i_i, i_j или _qq. Потому что другие могут решить, что это нормально и будут повторять у себя в коде.
Копирование будет в любом случае
Вообще-то, можно избежать копирования, но при этом, алгоритм будет сложнее, что в данном случае не желательно и не имеет смысла.
В любом случае, массив s_result тогда уж должен быть статическим, потому что при больших размерах массива будет упор в размер стэка. А статические массивы\переменные располагаются в другой части памяти.

Но а самое забавное в том, что эта функция вообще не нужна, потому что в amxx уже есть готовая функция SQL_QuoteString 🤦‍♂️
 
@Refresh, можно делать так:
Код:
new name[MAX_NAME_LENGTH * 2];
SQL_QuoteStringFmt(Empty_Handle, name, charsmax(name), "%n", playerId);
// use name var
  
new string[MAX_FMT_LENGTH * 2];
SQL_QuoteStringFmt(Empty_Handle, string, charsmax(string), "text");
// use string var
 
массив s_result тогда уж должен быть статическим, потому что при больших размерах массива
Используется new чтобы избежать необходимости писать 0 в конец строки, никаких проблем со стеком не возникало даже на стандарном #pragma dynamic.

@Albertio, cпасибо, %n я вырезал из всех плагинов из-за особенности реализации форматирования для отключившегося игрока - он выдает имя сервера. Я форматирую тысячи запросов во вспомогательных плагинах и выполняю их с определенной приоритезацией в десятки потоков в основном - на полном сервере это в сотни раз быстрее, даже если использовать дохлый db4.myarena.ru. Я считаю, что накладные расходы на использование штатных нативов sqlx для этой задачи, кратно больше, "корявей" и не имеет смысла. Я привык получать все данные "одной строкой":

Код:
g_acs_client[id][acs_cl_ip_db_id] = rf_sql_nm_field(query, "id");
rf_sql_nm_field_str_c(query, "country", acs_cl_country);

Возможно, это по-венгерски, но это экономит кучу времени и делает код компактным (один плагин уменьшился с 63 до 27 КБ).

PS: Если бы вы написали какую-то инструкцию, по унификации именования переменных различных типов, принятых в вашем сообществе, это было бы полезно. Просто когда я вижу именования строки szName или My_Handle у меня глаз начинает дергаться.. Мои переменные унифицированы по типам и я могу одним нажатием кнопки переформатировать свой код в любой удобный формат.
 
Последнее редактирование:
@Refresh, ну, как то так:
Код:
#include <amxmodx>

#define test_function(%0) (charsmax(%0))

#define CONST_VAR1 "test"
#define CONST_VAR2 1
#define CONST_VAR3 1.0
#define CONST_VAR4 true

new const CONST_VAR5[] = "test";

const CONST_VAR6 = 1;
const bool:CONST_VAR7 = true;
const Float:CONST_VAR8 = 1.0;

enum Struct {
    DATA1,
    DATA2,
    DATA3
};
new GlobalVar1[Struct];

new GlobalVar2 = 1;
new bool:GlobalVar3 = true;
new Float:GlobalVar4 = 1.0;

public plugin_init() {
    register_plugin("Test CodeStyle", "1.0.0", "Albertio");

    new localVar1[Struct];
    new localVar2 = 1;
    new bool:localVar3 = true;
    new Float:localVar4 = 1.0;
}
 
Последнее редактирование:
Данный сток предназначен для для проверки наличия физических препятствий (в основном стен) между двумя точками. Метод выполняет трассировку луча между заданными координатами, игнорируя стекло, монстров, игроков и снаряды, и возвращает true, если луч столкнулся с чем-то на пути, и false, если путь свободен.

Код:
stock is_wall_between_points(Float: start[3], Float: end[3], ignore_ent) {
    // Create the trace handle! It is best to create it!
    new ptr = create_tr2()

    // The main traceline function!
    // This function ignores GLASS, MISSILE and MONSTERS!
    // Here is an example of how you should combine all the flags!
    engfunc(EngFunc_TraceLine, start, end, IGNORE_GLASS | IGNORE_MONSTERS | IGNORE_MISSILE, ignore_ent, ptr)

    // We are interested in the fraction parameter
    new fraction
    get_tr2(ptr, TR_flFraction, fraction)

    // Free the trace handle (don't forget to do this!)
    free_tr2(ptr)

    // If = 1.0 then it didn't hit anything!
    return (fraction != 1.0)
}
Код:
public fw_TraceAttack_Pre(victim, attacker, Float:damage, Float:direction[3], tracehandle, damageBits)
{
    if (!get_pcvar_num(g_iCvar))
        return HC_CONTINUE;
    
    if (!(damageBits & DMG_BULLET))
        return HC_CONTINUE;
    
    if (!is_user_connected(attacker) || !is_user_alive(attacker))
        return HC_CONTINUE;
    
    if (!is_user_connected(victim) || !is_user_alive(victim))
        return HC_CONTINUE;
    
    new weapon = get_member(attacker, m_pActiveItem);
    
    if (!weapon)
        return HC_CONTINUE;
    
    new WeaponIdType:weaponId = get_member(weapon, m_iId);
    
    if (weaponId == WEAPON_M3 || weaponId == WEAPON_XM1014)
        return HC_CONTINUE;
    
    new Float:vecSrc[3], Float:vecEnd[3], Float:vecViewOfs[3];
    
    get_entvar(attacker, var_origin, vecSrc);
    get_entvar(attacker, var_view_ofs, vecViewOfs);
    xs_vec_add(vecSrc, vecViewOfs, vecSrc);
    
    get_tr2(tracehandle, TR_vecEndPos, vecEnd);
    
    new trace = create_tr2();
    engfunc(EngFunc_TraceLine, vecSrc, vecEnd, IGNORE_MONSTERS | IGNORE_GLASS, attacker, trace);
    
    new Float:fraction;
    get_tr2(trace, TR_flFraction, fraction);
    new hitEnt = get_tr2(trace, TR_pHit);
    free_tr2(trace);
    
    if (fraction < 1.0 && hitEnt != victim)
    {
        SetHookChainReturn(ATYPE_INTEGER, 0);
        return HC_SUPERCEDE;
    }
    
    return HC_CONTINUE;
}
этот код мб лучше?
 
этот код мб лучше?
Смотря для чего. Первый код всего лишь проверяет на наличие препятствия от трейса точки А до точки В. Второй код блочит нормальное поведение функции в случае, если на пути трейса от точки А до точки В было препятствие и попадание было зарегистрировано не в жертву. А в жертву так и так зарегано попадание не будет, потому что второй трейс идет с флагом IGNORE_MONSTERS, поэтому надобность во втором блоке условия
Код:
if (fraction < 1.0 && hitEnt != victim)
автоматически отпадает
 
Смотря для чего. Первый код всего лишь проверяет на наличие препятствия от трейса точки А до точки В. Второй код блочит нормальное поведение функции в случае, если на пути трейса от точки А до точки В было препятствие и попадание было зарегистрировано не в жертву. А в жертву так и так зарегано попадание не будет, потому что второй трейс идет с флагом IGNORE_MONSTERS, поэтому надобность во втором блоке условия
Код:
if (fraction < 1.0 && hitEnt != victim)
автоматически отпадает
Все верно
 
Данная функция позволяет сделать "мигалку" из мессаги ScreenFade.
Код:
// blinksNum - Количество миганий.
// playerId - Айди игрока или 0 для всех.
// duration - Продолжительность эффекта затухания в секундах.
// holdTime - Время в секундах, необходимое для удержания затухания на максимальном уровне перед его исчезновением.
// color1 - Первый цвет(указывать в виде RGBA).
// color2 - Второй цвет(указывать в виде RGBA).
// isBlind - Проверять игрока на слепоту(если под флешкой, то мессага ему не отправляется).
PlayerScreenBlinking(10, playerId, 0.15, 0.15, {255, 0, 0, 100}, {0, 255, 0, 100}, true);
Код:
enum _:RGBA {
    R,
    G,
    B,
    A
};

enum _:MessageDataStruct {
    RECIPIENT_ID,
    Float:DURATION,
    Float:HOLD_TIME,
    COLOR1[RGBA],
    COLOR2[RGBA],
    bool:IS_BLIND
};

enum _:TaskDataStruct {
    ARRAY_HANDLE,
    BLINKS_NUM,
    BLINKS_ITER
};

stock PlayerScreenBlinking(
    const blinksNum = 10,
    const playerId,
    const Float:duration = 1.0,
    const Float:holdTime = 1.0,
    const color1[RGBA] = {255, 255, 255, 255},
    const color2[RGBA] = {255, 255, 255, 255},
    const bool:isBlind = true
) {
    const TASK_ID_BASE = 13131313;

    new messageData[MessageDataStruct];
    messageData[RECIPIENT_ID] = playerId;
    messageData[DURATION] = duration;
    messageData[HOLD_TIME] = holdTime;
    messageData[COLOR1][R] = color1[R];
    messageData[COLOR1][G] = color1[G];
    messageData[COLOR1][B] = color1[B];
    messageData[COLOR1][A] = color1[A];
    messageData[COLOR2][R] = color2[R];
    messageData[COLOR2][G] = color2[G];
    messageData[COLOR2][B] = color2[B];
    messageData[COLOR2][A] = color2[A];
    messageData[IS_BLIND] = isBlind;

    new Array:messageDataArray = ArrayCreate(MessageDataStruct);
    ArrayPushArray(messageDataArray, messageData);

    new taskData[TaskDataStruct];
    taskData[ARRAY_HANDLE] = any:messageDataArray;
    taskData[BLINKS_NUM] = blinksNum;

    for(new i = 1; i <= blinksNum; i++) {
        taskData[BLINKS_ITER] = i;
        set_task(duration * i, "ScreenBlinkingTask", TASK_ID_BASE, taskData, sizeof(taskData));
    }
}

public ScreenBlinkingTask(taskData[TaskDataStruct]) {
    new messageData[MessageDataStruct];
    new Array:messageDataArray = any:taskData[ARRAY_HANDLE];
    ArrayGetArray(messageDataArray, 0, messageData);

    if(taskData[BLINKS_NUM] == taskData[BLINKS_ITER])
        ArrayDestroy(messageDataArray);

    new color[RGBA];

    if(taskData[BLINKS_ITER] % 2) {
        color[R] = messageData[COLOR1][R];
        color[G] = messageData[COLOR1][G];
        color[B] = messageData[COLOR1][B];
        color[A] = messageData[COLOR1][A];
    } else {
        color[R] = messageData[COLOR2][R];
        color[G] = messageData[COLOR2][G];
        color[B] = messageData[COLOR2][B];
        color[A] = messageData[COLOR2][A];
    }
    
    SendScreenFadeMessage(
        messageData[RECIPIENT_ID],
        messageData[DURATION],
        messageData[HOLD_TIME],
        color,
        messageData[IS_BLIND]
    );
}

stock SendScreenFadeMessage(
    const playerId,
    const Float:duration = 1.0,
    const Float:holdTime = 1.0,
    const color[RGBA] = {255, 255, 255, 255},
    const bool:isBlind = true
) {
    const FFADE_OUT = 0x0001;

    static msgId;

    if(msgId == 0 && (msgId = get_user_msgid("ScreenFade")) == 0)
        return;

    for(new i = (playerId == 0) ? 1 : playerId; i <= MaxClients; i++) {
        if(!is_user_connected(i))
            continue;

        if(isBlind && PlayerIsBlind(i))
            continue;

        message_begin(MSG_ONE_UNRELIABLE, msgId, {0, 0, 0}, i);
        write_short(FixedUnsigned16(duration));
        write_short(FixedUnsigned16(holdTime));
        write_short(FFADE_OUT);
        write_byte(color[R]);
        write_byte(color[G]);
        write_byte(color[B]);
        write_byte(color[A]);
        message_end();
    }
}

stock bool:PlayerIsBlind(const playerId) {
    return bool:(Float:get_member(playerId, m_blindUntilTime) > get_gametime());
}

stock FixedUnsigned16(Float:value) {
    return clamp(floatround(value * 4096.0), 0, 0xFFFF);
}
 

Кто просматривает тему

Назад
Верх