Вход

Просмотр полной версии : [Вопрос] AMX error occurred in public pc_cmd_show_licenses: Array index out of bounds



ex4mpl3
24.01.2023, 18:08
Здравствуйте, решил написать простую систему лицензий, при вводе команды, часть отрабатывает нормально, а часть нет.

Команда.

CMD:show_licenses(playerid, params[])
{
new
targetid;

if (sscanf(params, "d", targetid))
{
return SendClientMessage(playerid, COLOR_WHITE, "Введите: /show_licenses ");
}

if (!IsPlayerConnected(targetid))
{
return SendClientMessage(playerid, COLOR_LIGHTRED, "Игрока с таким ником/ид нет в игре.");
}

ShowLicensesArray(playerid);
ShowPlayerLicenses(playerid, targetid);

return true;
}



enum E_LICENSES_TYPE
{
E_LICENSE_CAR,
E_LICNESE_AIRPLANE,
E_LICNESE_BOAT,
E_LICNESE_MOTORBIKE,
E_LICENSE_WEAPON,
E_LICENSE_BUSSINES
}

const
MAX_LICENSES_TYPE = _:E_LICENSES_TYPE,
MAX_LICENSE_NAME = 40;

new
bool:g_players_licenses[MAX_PLAYERS][E_LICENSES_TYPE] = {false, ...};

static const g_licenses_name[E_LICENSES_TYPE][MAX_LICENSE_NAME] =
{
"Водительские права",
"Лицензия на возждение самолётом",
"Лицензия на вождение водным транспортом",
"Лицензия на вождения мотоцкла",
"Лицензия на ношей оружия",
"Лицензия на ведения бизнеса"
};


ShowLicensesArray(playerid); - отрабатывает полностью нормально.

stock ShowLicensesArray(playerid)
{
for (new i = 0; i < MAX_LICENSES_TYPE; ++i)
{
printf("[ShowLicensesArray]: i: %d, lic: %d", i, g_players_licenses[playerid][i]);
}
}
GeneratePlayerLicenseList - доходит до строки (11) "[I]printf("[GeneratePlayerLicenseList]: i + 1: %d", i + 1);" отрабатывает и дальше не идёт.

stock GeneratePlayerLicenseList(playerid, output_string[512], const size = sizeof(output_string))
{
printf("[GeneratePlayerLicenseList]: ОТРАБОТАЛ");
output_string[0] = EOS;

format(output_string, size, "Лицензии игрока: %s",
GetPlayerName(playerid));

for (new i = 0; i < MAX_LICENSES_TYPE; ++i)
{
printf("[GeneratePlayerLicenseList]: i + 1: %d", i + 1);
printf("[GeneratePlayerLicenseList]: g_players_licenses[playerid][i]: %d)", g_players_licenses[playerid][i]);
printf("[GeneratePlayerLicenseList]: g_licenses_name[i]: %s", g_licenses_name[i]);
printf("[GeneratePlayerLicenseList]: (GetPlayerLicense(playerid, i): %d)", GetPlayerLicense(playerid, i));
printf("[GeneratePlayerLicenseList]: ДОШЛО!");
format(output_string, size, "%s%d. %s: %s\n",
output_string,
i + 1,
g_licenses_name[i],
(GetPlayerLicense(playerid, i)) ? "Есть": "Отсутствует");
}
}

Не понятно как такое происхсодит, т.к. 12 строка делаёт всё то же, что и цикл в ShowLicensesArray(playerid), но тот отрабатывает нормально, а это нет.

В крашлоге это:
[16:04:46] #0 00012ee8 in ?? (0, 4282148, 512) at C:ссылка\gamemodes\Tested.pwn:885 *(16 строка в GeneratePlayerLicenseList)*

и это:
[16:04:46] #1 00410334 in public pc_cmd_show_licenses () at C:\ссылка\gamemodes\Tested.pwn:38911 *(вообще в самый конец мода, который никак не связан)*

punkochel
24.01.2023, 18:47
Покажи как ты GeneratePlayerLicenseList вызываешь, с какими параметрами.

UPD:
GetPlayerName возвращает длину записанного никнейма, а не сам никнейм.
В данном случае тебе необходимо убедиться на 100% что размер массива который будет передан в функцию через параметр output_string[] будет обеспечивать запись всего форматируемого текста.

stock GeneratePlayerLicenseList(playerid, output_string[], size = sizeof(output_string))
{
GetPlayerName(playerid, output_string, MAX_PLAYER_NAME+1);
format(output_string, size, "Лицензии игрока: %s", output_string);
for(new i; i < MAX_LICENSES_TYPE; ++i) {
format(output_string, size, "%s%d. %s: %s\n",
output_string,
i+1,
g_licenses_name[i],
(GetPlayerLicense(playerid, i)) ? ("Есть") : ("Отсутствует"));
}
return 1;
}


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

Shaolinka
24.01.2023, 18:55
Во первых посоветовал бы не играться со стеком, выделяя память в том случае, где это вовсе не нужно. В твоём случае нет нужды сохранять названия подпунктов диалога с лицензиями в двумерном массиве. Это понты, раз уж на то пошло, на реальном примере, спустя время, будешь прибегать к наиболее оптимальному варианту, не потребляющему столько байтов. Дальше хотелось бы подметить то, что нельзя присваивать данные двумерному массиву таким образом, коим это делаешь Ты. Тем более, если речь о булевых значениях, то нет смысла делать такое, ибо пр-тумолчанию итак false будет. Хотя, если бы вторая мера не была перечислением, то можно было бы провернуть такое:



new bool: g_players_licenses[MAX_PLAYERS][MAX_LICENSES_TYPE] = {{false, ...}, ...};

но если речь о перечислении, то придётся тогда уж либо циклом присваивать всем элементам той или иной статус(вариант не из лучших), либо же создавать массив на n кол-во элементов(MAX_LICENSES_TYPE), устанавливая всем элементам false статус.

Насчёт проблемы, то почему не указан const и видеть бы, при каких условиях вызывается GeneratePlayerLicenseList. И к слову, не понимаю к чему очищать массив изначально. Судя по всему в output_string поступает глобальный массив, где вполне себе могут быть данные. К рассчёту памяти в этом случае тоже стоило бы подойти рациональнее, потому как не уверен, что там и впрямь всё 512 ячеек нужны. Ну и к слову, ты забыл скобки в тернарке, поэтому вот:



format(output_string, size, "%s%d. %s: %s\n",
output_string,
i + 1,
g_licenses_name[i],
(GetPlayerLicense(playerid, i) ? ("Есть") : ("Отсутствует")));


- - - Добавлено - - -


Покажи как ты GeneratePlayerLicenseList вызываешь, с какими параметрами.

UPD:
GetPlayerName возвращает длину записанного никнейма, а не сам никнейм.
В данном случае тебе необходимо убедиться на 100% что размер массива который будет передан в функцию через параметр output_string[] будет обеспечивать запись всего форматируемого текста.

stock GeneratePlayerLicenseList(playerid, output_string[], size = sizeof(output_string))
{
GetPlayerName(playerid, output_string, MAX_PLAYER_NAME+1);
format(output_string, size, "Лицензии игрока: %s", output_string);
for(new i; i < MAX_LICENSES_TYPE; ++i) {
format(output_string, size, "%s%d. %s: %s\n",
output_string,
i+1,
g_licenses_name[i],
(GetPlayerLicense(playerid, i)) ? ("Есть") : ("Отсутствует"));
}
return 1;
}


Если мне не изменяет память, то использовав формат в надежде записать: Лицензии игрока %s, ник не будет передан, ибо формат при записи в массив новых данных, чистит его от старых. Поэтому гораздо-более благонадежнее было бы создать отдельное хранилище под ник. А ещё лучше записывать его куда-нибудь, когда игрок подключается к серваку. И, к слову, длину output_string всё же стоило бы указать, ибо в более новых версиях компилятор реагирует на нефиксированный размер массив, выводя предупреждение

punkochel
24.01.2023, 19:41
Если мне не изменяет память, то использовав формат в надежде записать: Лицензии игрока %s, ник не будет передан, ибо формат при записи в массив новых данных, чистит его от старых. Поэтому гораздо-более благонадежнее было бы создать отдельное хранилище под ник. А ещё лучше записывать его куда-нибудь, когда игрок подключается к серваку. И, к слову, длину output_string всё же стоило бы указать, ибо в более новых версиях компилятор реагирует на нефиксированный размер массив, выводя предупреждение

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


Если мне не изменяет память, то использовав формат в надежде записать: Лицензии игрока %s, ник не будет передан, ибо формат при записи в массив новых данных, чистит его от старых. Поэтому гораздо-более благонадежнее было бы создать отдельное хранилище под ник.
format будет использовать значения, которые ему будут переданы. То есть заполнение стека произойдет в обратном направлении от записи параметров, поэтому output_string будет иметь свое значение до записи в себя-же.
Пример (обе функции дадут одинаковый результат):
stock Function()
{
new str[5+1];
for(new i; i < sizeof(str); i++) {
format(str, sizeof(str), "%s%i", str, i);
}
printf("%s", str); // Output: 01234
}

stock Function2()
{
new str[5+1],
str_val[1+1];
for(new i; i < sizeof(str); i++) {
valstr(str_val, i);
strcat(str, str_val);
}
printf("%s", str); // Output: 01234
}

ex4mpl3
25.01.2023, 14:18
Помогло, спасибо всем за помощь и советы

- - - Добавлено - - -


но если речь о перечислении, то придётся тогда уж либо циклом присваивать всем элементам той или иной статус(вариант не из лучших), либо же создавать массив на n кол-во элементов(MAX_LICENSES_TYPE), устанавливая всем элементам false статус.

Не понял какая разница, ведь если даже перечислитель - всё равно сработает.

new bool: g_players_licenses[MAX_PLAYERS][E_LICENSES_TYPE] = {{false, ...}, ...};

punkochel
25.01.2023, 16:48
Не понял какая разница, ведь если даже перечислитель - всё равно сработает.
new bool: g_players_licenses[MAX_PLAYERS][E_LICENSES_TYPE] = {{false, ...}, ...};

Сработает, но pawn уже по умолчанию установит 0/false для значения переменной/массива.
Хотя это актуально и для Си, за исключением локальных переменных. Поэтому непонятно откуда такой формат.
Это просто будет более лаконичнее выглядеть, и любому сразу станет ясно что тут хранятся false:
new bool:g_players_licenses[MAX_PLAYERS][E_LICENSES_TYPE];