F.A.Q. по скриптингу by Nordic Warrior

Сообщения
3,561
Реакции
1,559
Помог
131 раз(а)
Итак, наконец у меня дошли руки создать данную тему.
Предпосылки к созданию: Ищу "наставника" по amxx

Краткое описание: в этой теме я буду задавать интересующие меня вопросы по скриптингу, а после получения ответа, (если Sonyx немного поможет мне с таймингом редактирования поста:smile3:) ответы будут размещаться здесь, в шапке темы, по виду:

Q: Вопрос
A: (автор ответа) Ответ

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

Итак, мой первый вопрос: какое меню лучше? Которое создается через menu_create (я так понял, это т.н. "новое" меню?), или через register_menu? Попрошу дать развернутый ответ, почему то или иное меню лучше.

/* Начало F.A.Q. */
  • Q: Какое меню лучше? "Старое" или "новое"?
  • A: BlackSignature, gyxoBka: Это зависит от требуемых задач. Зачастую "новое" меню не может выполнить те задачи, которые может выполнить "старое", но иногда всё-же наоборот, удобнее использовать "новое" меню.

  • Q: В чём разница между static и new const?
  • A: BlackSignature: const делает переменную/массив read-only, т.е. можно читать, но нельзя изменять.
    static создаёт переменную при первом обращении в функции, и после завершения функции переменная не удаляется, т.е. сохраняет своё текущее значение.
    1) В очень часто вызываемых функциях (каллбеки пофреймовых хуков вроде PreThink, AddToFullPack и т.п.) позволяет сэкономить процессорное время на создании переменных/массивов. Особенно актуально для последних (чем они больше, тем больше выигрыш).
    2) Сохранение значения в локальных масштабах (в пределах функции).

  • Q; Что такое #pragma, и какие они бывают?
  • A; wopox1337: это директивы препроцессора. Диктуют компилятору условия.

  • Q: ShowSyncHudMsg() работает только внутри одного плагина, или синхронизирует весь худ со всеми плагинами, где он есть?
  • A: fantom: Всех. Ибо HUD имеет всего 4 канала. Вся работа SyncHud заключается в том, чтобы запомнить последний канал по которому было отправлено sync сообщение, и его же использовать при следующей отправке сообщения. Таким образом у вас не получится наложения предыдущего HUD на текущий. Ведь предыдущий будет заменен.
    При этом последний аргумент в set_hudmessage() можно не указывать.
  • Дополнение от w0w: Если в разных плагинах использовать CreateHudSyncObj() то при выводе худов из разных плагинов будет накладываться. Таким образом, необходимо передавать объект SyncHud'a в другие плагины с худом.

  • Q: Нужно ли использовать ClearSyncHud() всякий раз перед ShowSyncHudMsg()?
  • A: Ayk: Нет, не нужно. Суть синка в том чтобы не было наложений, значит предварительная очистка подразумевается.

  • Q: Что за операторы += и -= ? В чем их отличие от + и - ?
  • A: the_hunter: допустим, у тебя есть переменная new variable = 5;. Тебе нужно добавить какое-то значение к этой переменной. Как это сделать? Можно вот так: variable = variable + 10;. Но оператор += позволяет сократить код который выше, до такого: variable += 10;.
    То есть, += это синтаксический "сахар", который позволяет сократить код.

  • Q: За что отвечает pev_gaitsecuence?
  • A: Garey: за текущую анимацию нижней части модели игрока (для прыжков, бега, приседания и т.д.) на обычных энтитях не проигрывается и за чего криво анимации игрока выставляются при проигрывании.

  • Q: Какие лимиты по кол-ву символов у HUD/DHUD?
  • A: BalbuR: У обычного HUD 512, у DHUD 128.

  • Q: Чем отличаются ewrite_byte (и другие функции для работы с мессагами) от обычных, без е?
  • A: Ayk: Отправка месаг с "е" вызывает хуки этих месаг.

  • Q: В чем различие между записями new const SOME_CONSTANT и const SOME_CONSTATNT?
  • A: the_hunter: const - это константа, значение будет известно компилятору (compile-time). new const это константная переменная, которая будет создана во время работы плагина (run-time) и заранее компилятору не известно ее значение.
    Ключевое слово const в данном случае, говорит что этой переменной можно присвоить значение только 1 раз.

  • Q: Для чего у хуков есть функция Disable? И в каких случаях её стоит применять? Например, DisableHookChain.
  • A: fantom: Больше всего времени тратится на вызовы между C++ частью AMXX и Pawn. Допустим, у вас есть хук который часто вызывается. И есть момент только один в котором вам нужно например заблокировать вызов этого хука. Если делать без disable, то вам в хуке придется все время проверять ваше событие это или нет (например глобальная булевая переменная). Если вы просто отключите хук когда он вам не нужен и просто включите когда нужен, то это в некоторых случаях может существенно скажется на оптимизации плагина? Но стоит понимать: не всегда и везде подобное стоит применять. Есть моменты когда вы получите совсем обратный эффект.
    shadow: Например, тебе нужно будет выдать приз игроку первому, кто набрал 15 фрагов. Ты хукаешь событие Killed и сверяешь там количество фрагов убийцы и с необходимым количеством фрагов для выдачи приза. По достижении этого количества выдается приз. Все. Дальше необходимость в этом хуке отпадает. Тут мы и используем DisableHookChain.

  • Q: Где можно посмотреть список всех предопределённых сообщений для client_printex()?
  • A: Ayk, fl0wer: cstrike/titles.txt или cstrike/resource/cstrike_english.txt

  • Q: Как убрать квары, которые уже были зарегистрированы на сервере?
  • A: the_hunter: Отключить плагин, полностью перезагрузить сервер.

  • Q: Как можно пропустить итерацию цикла, при проверке во вложенном в него ещё одном цикле?
  • A: malniata: создать отдельную булевую функцию и вложить туда второй цикл, в первом же делать проверку на верно/неверно.
  • A: Mistrick: Код-пример
  • A: Ну и ещё один вариант, который я изначально пытался использовать, но у меня было неверное условие - это использование goto. Пример.
 
Последнее редактирование:
Сообщения
1,275
Реакции
2,257
Помог
57 раз(а)
Итак, мой первый вопрос: какое меню лучше? Которое создается через menu_create (я так понял, это т.н. "новое" меню?), или через register_menu? Попрошу дать развернутый ответ, почему то или иное меню лучше.
Вопрос не совсем понятен. Что подразумевается под "лучше"? Всё всегда зависит от задачи, а в данном случае, ещё и от предпочтения. Многостраничные меню (особенно, динамические, т.е. меняющиеся), как по мне, удобнее делать через систему "нового меню". Но это не всегда так, всё зависит от условий.
 
Сообщения
219
Реакции
184
Помог
3 раз(а)
Новая система меню это лишь обертка для "старого" меню и зачастую на новом меню нетипичные задачи не решить.
 
Сообщения
3,561
Реакции
1,559
Помог
131 раз(а)
BlackSignature, "лучше" для меня, в данном случае, удобнее, полезнее, надежнее.
Потому что сейчас я все свои меню пишу по старой системе. Вот и думаю, может я совершаю ошибку, и давно пора использовать новые меню?
15 Дек 2018
gyxoBka, иначе говоря, особой разницы нет? Кроме нюансов
15 Дек 2018
BlackSignature, вот у меня как раз в одном плагине многостраничное меню, в другом динамическое меню. (С переключателями вкл/выкл) сейчас они реализованы как несколько меню (страницы) и if-else для переключателей. Стоит переписать эти меню по новой системе?
 
Сообщения
1,275
Реакции
2,257
Помог
57 раз(а)
sbelov020, На данном этапе используйте тот вариант, который удобнее вам. Просто покажу пример старой системы, где при генерации составляется меню игроков + технические пункты, которые в системе "нового" меню описывались бы проще (меньше кода). В новой системе так же проще с определением страницы. Однако, конкретно этот вариант имеет ряд нюансов, которые не позволяют заюзать тут "новое меню" без лишних заморочек. Духовка верно подметил. Чем сложнее собирается меню, тем сложнее с ним работать при помощи "новой" системы.

Код:
stock func_TransferMenu(id, iPage = _PAGE1_) {
    new iPlayerCount, iPlayer

    for(iPlayer = 1; iPlayer <= MaxClients; iPlayer++) {
        if(iPlayer == id || !ps_is_player_loaded(iPlayer)) {
            continue
        }

        g_iPlayers[id][iPlayerCount] = iPlayer
        g_iPlayerIDs[id][iPlayerCount] = get_user_userid(iPlayer)
        iPlayerCount++
    }

    new i = min(iPage * ITEMS_PER_PAGE__TRANSFER_MENU, iPlayerCount)
    new iStart = i - (i % ITEMS_PER_PAGE__TRANSFER_MENU)
    new iEnd = min(iStart + ITEMS_PER_PAGE__TRANSFER_MENU, iPlayerCount)

    g_iMenuPage[id] = iPage = iStart / ITEMS_PER_PAGE__TRANSFER_MENU

    new iPos, iMenuItem, iKeys = MENU_KEY_0,
        iPagesNum = max(1, (iPlayerCount / ITEMS_PER_PAGE__TRANSFER_MENU + ((iPlayerCount % ITEMS_PER_PAGE__TRANSFER_MENU) ? 1 : 0)))

    new iCommission = g_eCvar[CVAR__COMM_PERCENT_USER], iFlags = get_user_flags(id)

    if(iFlags & g_eCvar[CVAR__SUPERVIP_FLAG]) {
        iCommission = g_eCvar[CVAR__COMM_PERCENT_SUPERVIP]
    }
    else if(iFlags & g_eCvar[CVAR__VIP_FLAG]) {
        iCommission = g_eCvar[CVAR__COMM_PERCENT_VIP]
    }

    new iLen = formatex( g_szMenu, chx(g_szMenu),
        "\yВыберите получателя [%d/%d]^n\
        \wУ вас \y%d \wочков \y(комиссия %d%%)^n^n",

        iPage + 1, iPagesNum,
        ps_get_points(id), iCommission
    );

    for(i = iStart; i < iEnd; i++) {
        iKeys |= (1 << iMenuItem)
        iPlayer = g_iPlayers[id][i]
        iLen += formatex(g_szMenu[iLen], chx(g_szMenu) - iLen, "\y%d. \w%n^n", ++iMenuItem, iPlayer)
    }

    if(!iMenuItem) {
        if(iPage) {
            return func_TransferMenu(id, --iPage)
        }

        g_iPlayers[id][0] = 0
        iKeys |= MENU_KEY_1
        iLen += formatex(g_szMenu[iLen], chx(g_szMenu) - iLen, "\y1. \dНет подходящих \y[обновить]")
    }

    if(iPage) {
        iPos++
        iKeys |= MENU_KEY_8
        iLen += formatex(g_szMenu[iLen], chx(g_szMenu) - iLen, "^n\y8. Назад")
    }

    if(iEnd < iPlayerCount)    {
        iPos++
        iKeys |= MENU_KEY_9
        iLen += formatex(g_szMenu[iLen], chx(g_szMenu) - iLen, "^n\y9. Далее")
    }

    formatex(g_szMenu[iLen], chx(g_szMenu) - iLen, "%s^n\y0. Выход", iPos ? "^n" : "")

    return func_ShowMenu(id, iKeys, MENU_MODE__TRANSFER)
}

stock func_TransferMenu_SubHandler(id, iKey) {
    new iMenuPage = g_iMenuPage[id]

    switch(iKey) {
        case _KEY8_: func_TransferMenu(id, --iMenuPage)
        case _KEY9_: func_TransferMenu(id, ++iMenuPage)
        case _KEY0_: return PLUGIN_HANDLED
        default: { // _KEY1_.._KEY7_
            new iPos = (iMenuPage * ITEMS_PER_PAGE__TRANSFER_MENU) + iKey
            new iTarget = g_iPlayers[id][iPos]
            new iTargetID = get_user_userid(iTarget) // SafeToUse для отключённых (возвращает -1)

            if(iTargetID != g_iPlayerIDs[id][iPos] || !g_iPlayers[id][0] || !ps_is_player_loaded(id))
                return func_TransferMenu(id, iMenuPage)

            g_iTarget[id] = iTarget
            g_iPlayerIDs[id][0] = iTargetID
            g_iInputMode[id] = INPUT_MODE__TRANSFER

            rg_send_audio(id, SOUND__BLIP1)
            client_print_color(id, iTarget, "^4* ^1Напишите в чат, сколько очков вы хотите передать игроку^3 %n", iTarget)
            client_print_color(id, print_team_default, "^4* ^1Если передумаете, напишите^4 %s ^1для отмены запроса", g_szCancelCmd)
        }
    }

    return PLUGIN_HANDLED
}
 
Сообщения
333
Реакции
290
Помог
9 раз(а)
В новом меню нельзя поставить номер пункта в квадратные скобочки :smile3:
 
Сообщения
3,561
Реакции
1,559
Помог
131 раз(а)
Следующий вопрос: в чём разница между static и new const?
 
Последнее редактирование:
Сообщения
1,275
Реакции
2,257
Помог
57 раз(а)
sbelov020, const делает переменную/массив read-only, т.е. можно читать, но нельзя изменять.
Вне функций нет разницы, static или new используйте new (причина через один пост ниже)
Внутри функции:
new заново создаёт переменную при каждом обращении к функции, и соотвественно, после завершения функции переменная удаляется, освобождая занятую память (значение пропадает).
static создаёт переменную при инициализации, и после обращения к функции переменная не удаляется, т.е. сохраняет своё текущее значение.

Практическая польза от static внутри функции:
1) В очень часто вызываемых функциях (каллбеки пофреймовых хуков вроде PreThink, AddToFullPack и т.п.) позволяет сэкономить процессорное время на создании переменных/массивов. Особенно актуально для последних (чем они больше, тем больше выигрыш).
2) Сохранение значения в локальных масштабах (в пределах функции).
Код:
#include <amxmodx>

public plugin_init() {
    register_srvcmd("test", "myfunc")
}

public myfunc() {
    static iStatic
    new iNonStatic
 
    server_print("BEFORE -> iStatic: %d | iNonStatic: %d", iStatic, iNonStatic)
 
    iStatic++
    iNonStatic++

    server_print("AFTER -> iStatic: %d | iNonStatic: %d", iStatic, iNonStatic)
}
 
Последнее редактирование:
Сообщения
584
Реакции
1,006
Помог
18 раз(а)
В очень часто вызываемых функциях (каллбеки пофреймовых хуков вроде PreThink, AddToFullPack и т.п.) позволяет сэкономить процессорное время на создании переменных/массивов. Особенно актуально для последних (чем они больше, тем больше выигрыш).
Мнимый выигрыш, мало чем подтвержденный. Что будет, если статик осел в ОЗУ, а new в кэш процессора?
 
Сообщения
1,275
Реакции
2,257
Помог
57 раз(а)
Mistrick, может и так. Я не мерил, просто рассказал о применении как "деды с оленей завещали".
wopox1337, Какие файлы? Не понял. Переменная, объявленная в пределах функции, недоступна за пределами этой функции, если только она прямо не передаётся из этой функции куда-то наружу в виде аргумента. Ну это ты и так знаешь, это я ТС'у, чтобы понимал. :)
 
Сообщения
1,275
Реакции
2,257
Помог
57 раз(а)
wopox1337, Да, проверил, так и есть. Не знал об этом, т.к. никогда статики вне функций не использовал.
 
Сообщения
2,750
Реакции
3,013
Помог
61 раз(а)
Если я правильно понял вопрос.
Код:
#define FIELD "Field #1"
или
new const FIELD[] = "Field #1";
То, лучше пользоваться вариантом new const. Почему? Проблемы компилятора AMXX по сей день не решены, хоть и не критичны.
Цитата от Prostotema:
Для строк в AMXX лучше использовать константы, потому что компилятор настолько божественен, что он записывает несколько одинаковых строчек (если это не константы) в .DATA
В компиляторах C++ такого мне не встречалось.
P.S: только не в угоду функциональности. (Там, где комментированием макроса можно повлиять на настройку)
 
Сообщения
3,561
Реакции
1,559
Помог
131 раз(а)
Если я правильно понял вопрос.
про дефайн тоже интересно, но

new const это только для массива?

меня интересует разница между объявлением аргументов через constили static
 
Сообщения
1,275
Реакции
2,257
Помог
57 раз(а)
sbelov020, я же объяснил уже всё. const означает что значение после инициализации уже нельзя изменить. А static, созданный в рамках функции, означает что "объект" не удаляется из памяти, а продолжает в ней висеть, и при следующем к нему обращении(при чтении) можно получить его значение, которое никуда не делось.
new iVar = 1
const iVar = 1
static iVar = 1
static const iVar = 1
Суньте себе в тестовый плагин и поиграйтесь (запись, чтение), и всё станет ясно.
 
Сообщения
3,561
Реакции
1,559
Помог
131 раз(а)
Следующий вопрос:

Объясните, пожалуйста, что такое, какие бывают #pragma, и в каких случаях их нужно применять?

Пока что знаю только #pragma semicolon 1 - чтоб компилятор был требователен к точкам с запятой после каждой строки
и #pragma tabsize 0 - при компиляции размер табуляции устанавливается в 0.

Еще видел #pramga unused <..> - это чтоб в какой-то функции не использовать указанные аргументы?

И еще где-то видел #pragma compress 1 - но что он делает, мне не ясно.
 
Сообщения
2,750
Реакции
3,013
Помог
61 раз(а)

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

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