Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Показано с 1 по 10 из 10

Тема: random switch

  1. #1
    Аватар для vvw
    Пользователь

    Статус
    Оффлайн
    Регистрация
    09.08.2019
    Сообщений
    33
    Репутация:
    6 ±

    random switch

    Данная библиотека позволяет создавать random switch, который выбирает одно из существующих значений.

    Исходный код:
    1. #include <amx/amx_base>
    2. stock _random_switch()
    3. {
    4. static base_addr = 0, temp_addr, randvalue;
    5. if (0 == base_addr) {
    6. base_addr = GetAmxBaseAddress();
    7. } {}
    8.  
    9. #emit load.s.alt 4
    10. #emit lctrl 0
    11. #emit add
    12. #emit move.alt
    13. #emit lctrl 1
    14. #emit sub.alt
    15. #emit add.c 4
    16. #emit stor.pri temp_addr
    17. #emit lref.pri temp_addr
    18. #emit stor.pri temp_addr
    19.  
    20. #emit load.alt base_addr
    21. #emit lctrl 1
    22. #emit add
    23. #emit move.alt
    24. #emit load.pri temp_addr
    25. #emit sub
    26. #emit add.c 4
    27. #emit stor.pri temp_addr
    28. #emit push.pri
    29. #emit lref.pri temp_addr
    30. #emit stor.pri temp_addr
    31.  
    32. randvalue = random(temp_addr) + 1;
    33.  
    34. #emit load.pri randvalue
    35. #emit shl.c.pri 3
    36. #emit pop.alt
    37. #emit add
    38. #emit stor.pri temp_addr
    39. #emit lref.pri temp_addr
    40. #emit retn
    41. return 0;
    42. }
    43.  
    44. #define switchrand() \
    45.   switch(_random_switch())


    Использование:
    1. main()
    2. {
    3. switchrand() {
    4. case 123: {printf("123\n");}
    5. case 321: {printf("321\n");}
    6. default: {} // не учитывает
    7. }
    8. }


    Идея: DeimoS
    Реализация: VVWVV

    P.S.: код скорее всего будет обновлен, ибо я отвык от AMX инструкций.

  2. 2 пользователя(ей) сказали cпасибо:
    DeimoS (13.11.2019)Osetin (13.11.2019)
  3. #2
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,040
    Репутация:
    2436 ±
    Задумка хорошая, но хотелось бы отметить несколько моментов:
    • В текущем виде пример использования только объясняет, что такое random switch , но не где это может пригодиться. Очень не помешал бы более-менее реалистичный пример.
      DeimoS ?
    • Если не сейчас, то в будущем код следует перевести на __emit. Если сейчас выгода не так очевидна, то в следующем релизе компилятора с новыми псевдоинструкциями можно будет здорово упростить код: например, псевдоинструкции load.u.pri/alt можно скармливать целые выражения, причём не только константные, но и вообще любые, даже с вложенным использованием __emit.
    • Вместо
      1. #emit stor.pri temp_addr
      2. #emit lref.pri temp_addr
      3. #emit stor.pri temp_addr

      можно было сделать
      1. #emit load.i
      2. #emit stor.pri temp_addr
    • Нельзя ли как-нибудь обойтись без функции GetAmxBaseAddress() (а заодно и выкинуть зависимость от amx_assembly, откуда используется только эта единственная функция)?
      Да и вообще, зачем здесь нужен базовый адрес AMX? Можно же просто получить смещение секции кода относительно секции данных (lctrl 1, move.alt, lctrl 0, sub), затем к нему прибавить адрес возврата из функции - по этому адресу и будет располагаться инструкция switch.
    Индивидуально в PM и Skype по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  4. #3
    Аватар для vvw
    Пользователь

    Статус
    Оффлайн
    Регистрация
    09.08.2019
    Сообщений
    33
    Репутация:
    6 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Задумка хорошая, но хотелось бы отметить несколько моментов:[list]
    В текущем виде пример использования только объясняет, что такое random switch , но не где это может пригодиться. Очень не помешал бы более-менее реалистичный пример.
    DeimoS ?
    Я надеюсь, что DeimoS добавит примеры.

    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Если не сейчас, то в будущем код следует перевести на __emit. Если сейчас выгода не так очевидна, то в следующем релизе компилятора с новыми псевдоинструкциями можно будет здорово упростить код: например, псевдоинструкции load.u.pri/alt можно скармливать целые выражения, причём не только константные, но и вообще любые, даже с вложенным использованием __emit.
    Когда будет реализ - сделаю.

    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Вместо
    1. #emit stor.pri temp_addr
    2. #emit lref.pri temp_addr
    3. #emit stor.pri temp_addr

    можно было сделать
    1. #emit load.i
    2. #emit stor.pri temp_addr
    Была бы ошибка, не?

    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Нельзя ли как-нибудь обойтись без функции GetAmxBaseAddress() (а заодно и выкинуть зависимость от amx_assembly, откуда используется только эта единственная функция)?
    Да и вообще, зачем здесь нужен базовый адрес AMX? Можно же просто получить смещение секции кода относительно секции данных (lctrl 1, move.alt, lctrl 0, sub), затем к нему прибавить адрес возврата из функции - по этому адресу и будет располагаться инструкция switch.
    Вот алгоритм получения адреса инструкции switch:
    1. #emit load.s.alt 4
    2. #emit lctrl 0
    3. #emit add
    4. #emit move.alt
    5. #emit lctrl 1
    6. #emit sub.alt
    7. #emit add.c 4
    8. #emit stor.pri temp_addr
    9. #emit lref.pri temp_addr
    10. #emit stor.pri temp_addr


    base_addr используется для нахождения относительного адреса из switch.

    Код:
    switch <абсолютный адрес на casetbl>
    jump
    ...
    casetbl <кол-во кейсов> <адрес по-умолчанию>
    Чуть позже я перепишу без зависимостей.

    P.S.: код скорее всего будет обновлен, ибо я отвык от AMX инструкций.
    Последний раз редактировалось vvw; 14.11.2019 в 12:37.

  5. #4
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,255
    Репутация:
    1860 ±
    Из более простых и очевидных примеров -собственно, любые системы, где нужно выбрать рандомно один из элементов switch.
    Например, решили мы сделать пикап, который бы выдавал рандомный приз:
    1. switch(random(6))
    2. {
    3. case 0:
    4. {
    5. new money = random(4000)+1000;
    6. GivePlayerMoney(playerid, money);
    7. new string[14+4+1];
    8. format(string, sizeof(string), "Вы получили $%d.", money);
    9. SendClientMessage(playerid, -1, string);
    10. }
    11. case 1:
    12. {
    13. new money = random(2000)+1000;
    14. GivePlayerMoney(playerid, -money);
    15. new string[14+4+1];
    16. format(string, sizeof(string), "Вы потеряли $%d.", money);
    17. SendClientMessage(playerid, -1, string);
    18. }
    19. case 2:
    20. {
    21. #if !defined FLOAT_INFINITY
    22. const Float:FLOAT_INFINITY = Float:0x7F800000;
    23. #endif
    24. SetPlayerHealth(playerid, FLOAT_INFINITY);
    25. SendClientMessage(playerid, -1, "Вы получили бессмертие (до перезахода).");
    26. }
    27. case 3:
    28. {
    29. GivePlayerWeapon(playerid, WEAPON_DEAGLE, 300);
    30. SendClientMessage(playerid, -1, "Вы получили пистолет.");
    31. }
    32. case 4:
    33. {
    34. new Float:x, Float:y, Float:z, Float:a;
    35. GetPlayerPos(playerid, x, y, z);
    36. GetPlayerFacingAngle(playerid, a);
    37.  
    38. new vehicleid = CreateVehicle(562, x, y, z, a, -1, -1, 1*60);
    39. SetVehicleParamsEx(vehicleid,
    40. VEHICLE_PARAMS_ON, VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF,
    41. VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF,
    42. VEHICLE_PARAMS_OFF);
    43. PutPlayerInVehicle(playerid, vehicleid, 0);
    44. SendClientMessage(playerid, -1, "Вы получили а-а-а-втомобиль.");
    45. }
    46. case 5:
    47. {
    48. new Float:x, Float:y, Float:z, Float:a;
    49. GetPlayerPos(playerid, x, y, z);
    50. GetPlayerFacingAngle(playerid, a);
    51.  
    52. new vehicleid = CreateVehicle(562, x, y, z, a, -1, -1, 1*60);
    53. SetVehicleParamsEx(vehicleid,
    54. VEHICLE_PARAMS_ON, VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF,
    55. VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF, VEHICLE_PARAMS_OFF,
    56. VEHICLE_PARAMS_OFF);
    57. PutPlayerInVehicle(playerid, vehicleid, 0);
    58. SendClientMessage(playerid, -1, "Вы получили а-а-а-втомобиль.");
    59. }
    60. }

    И всё, вроде бы, хорошо, система работает, а игроки довольны. Но тут мы, в одном из обновлений, решили убрать элемент под номером 1, ибо игроки расстраиваются, когда у них отбирают деньги. Если мы решим просто удалить его - система, фактически, станет нерабочей, ибо "random" иногда будет выдавать единицу. Чтоб всё исправить, придётся сделать что-то одно:
    1) Добавить первый элемент на какой-то из существующих призов (то бишь, сделать "case 0, 1:"), но это, фактически, увеличит шанс выпадения того или иного приза.
    2) Добавить какой-то новый приз вместо удалённого (придётся думать, что, естественно, большущий минус )
    3) Сместить номера всех элементов, которые шли после единицы + исправить значение в "random"
    4) Придумать костыль, по типу добавления goto, который бы отправлял обработчик в позицию перед switch, дабы random сгенерировало новое число.

    И если на подобном switch с малым количеством элементов все перечисленные пункты могут показаться незначительными, то представьте, что switch у вас состоит из 50, 100 и т.д. элементов. В этом случае switchrand довольно сильно упростит поддержку кода, сняв с вас и нужду думать насчёт того, чтоб все элементы были по порядку, и нужду в изменении значения, указанного в "random" при изменении количества элементов.


    Более специфичным примером будет пример, схожий с тем, что уже предоставлен в шапке темы: когда в switch находятся значения с большим разбросом, а не идут по порядку.
    Например, мы делаем RP мод с системой фракций и нам нужно отправить игрока в одну из трёх армий после выполнения одного из квестов. При этом, так получилось, что ID армий в списке фракций - 2, 15, 22.
    И тут есть 2 пути решения без switchrand:
    1) Поместить все нужные данные в массивы, определять рандомно индекс и уже отображать игроку нужную информацию из массивов согласно получившемуся индексу (но если у нас для каждой отдельной фракции нужно отображать уникальный код, могут возникнуть проблемы. Да и в целом код получится довольно сложным и менее гибким, относительно switchrand).
    2) Сделать нечто подобное:
    1. new army_frac[] = {2, 15, 22};
    2. new fid = random(sizeof(army_frac));
    3. switch(fid)
    4. {
    5. case 2:
    6. {
    7.  
    8. }
    9. case 15:
    10. {
    11.  
    12. }
    13. case 22:
    14. {
    15.  
    16. }
    17. }

    Но тут придётся следить за тем, чтоб значения в массиве и номера элементов совпадали, что может быть проблемно (особенно в моменты когда элементы наполнятся кодом и/или количество элементов возрастёт).


    В общем, главная задача switchrand - упростить работу со связкой "random+switch", перекладывая слежение за номерами элементов и числом в "random" со своей головы на компилятор. С switchrand единственное требование к коду будет заключаться в том, чтоб все элементы были уникальными (собственно, как и в switch). А уже по порядку они идут или имеются пробелы между значениями - не будет критично.
    Надеюсь, изложил понятно основную задумку всего этого.
    Связаться со мной в VK можно через личные сообщения этой группы

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

  6. #5
    Аватар для vvw
    Пользователь

    Статус
    Оффлайн
    Регистрация
    09.08.2019
    Сообщений
    33
    Репутация:
    6 ±
    Кстати, может кто-нибудь проверить работоспособность данной библиотеки с плагином JIT?

  7. #6
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,040
    Репутация:
    2436 ±
    Цитата Сообщение от vvw Посмотреть сообщение
    Кстати, может кто-нибудь проверить работоспособность данной библиотеки с плагином JIT?
    Не работает и в текущем виде работать не сможет. Функция _random_switch() узнаёт адрес инструкции switch (а по ней и таблицы переходов) по адресу возврата из функции, но проблема в том, что в качестве адреса возврата JIT передаёт не "виртуальный" адрес в секции кода, а физический адрес в сгенерированном нативном коде. И если виртуальный адрес можно преобразовать в физический (загрузить адрес в PRI, затем выполнить lctrl 8), то средств для преобразования физического адреса в виртуальный не предусмотрено.
    Единственный способ обойти это ограничение - в месте вызова функции получить значение CIP (lctrl 6) и затем его передать в _random_switch(). Но #emit из макроса не поюзаешь, только оператор __emit, а значит придётся пожертвовать совместимостью со стоковым компилятором (впрочем, кто им сейчас пользуется?)

    На данный момент я сделал это и ещё несколько изменений (немного изменил принцип работы функции, убрал зависимость от amx_assembly) и хочу выложить всё в виде готового инклуда. Каким образом мне записывать тебя в копирайтах? ("VVWVV"? Настоящее имя?) И ты не против будешь, если я выложу код под лицензией zlib?
    Индивидуально в PM и Skype по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  8. #7
    Аватар для Steve_Stage
    Пользователь

    Статус
    Онлайн
    Регистрация
    05.10.2019
    Сообщений
    137
    Репутация:
    0 ±
    Кстати, хотелось бы узнать насчет данной строчки

    1. #include <amx/amx_base>


    Это как? Два разных инклуда подключаются через слэш... Мб я не догоняю, но откуда такой инклуд?

    P.S. Как я понял, эта строка подключается к этим инклудам: https://pro-pawn.ru/showthread.php?15075
    Последний раз редактировалось Steve_Stage; 01.12.2019 в 18:24.

  9. #8
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,255
    Репутация:
    1860 ±
    Цитата Сообщение от Steve_Stage Посмотреть сообщение
    Кстати, хотелось бы узнать насчет данной строчки

    1. #include <amx/amx_base>


    Это как? Два разных инклуда подключаются через слэш... Мб я не догоняю, но откуда такой инклуд?

    P.S. Как я понял, эта строка подключается к этим инклудам: https://pro-pawn.ru/showthread.php?15075
    Это не два разных инклуда, а папка, в которой находится инклуд, и сам файл. Ты что, никогда не видел как путь до файлов прописывается в адресной строке?
    Связаться со мной в VK можно через личные сообщения этой группы

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

  10. #9
    Аватар для Steve_Stage
    Пользователь

    Статус
    Онлайн
    Регистрация
    05.10.2019
    Сообщений
    137
    Репутация:
    0 ±
    Цитата Сообщение от DeimoS Посмотреть сообщение
    Ты что, никогда не видел как путь до файлов прописывается в адресной строке?
    Я видел путь к файлу в адресной строке, но не знал, что также можно и с инклудами

  11. #10
    Аватар для vvw
    Пользователь

    Статус
    Оффлайн
    Регистрация
    09.08.2019
    Сообщений
    33
    Репутация:
    6 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Не работает и в текущем виде работать не сможет. Функция _random_switch() узнаёт адрес инструкции switch (а по ней и таблицы переходов) по адресу возврата из функции, но проблема в том, что в качестве адреса возврата JIT передаёт не "виртуальный" адрес в секции кода, а физический адрес в сгенерированном нативном коде. И если виртуальный адрес можно преобразовать в физический (загрузить адрес в PRI, затем выполнить lctrl 8), то средств для преобразования физического адреса в виртуальный не предусмотрено.
    Единственный способ обойти это ограничение - в месте вызова функции получить значение CIP (lctrl 6) и затем его передать в _random_switch(). Но #emit из макроса не поюзаешь, только оператор __emit, а значит придётся пожертвовать совместимостью со стоковым компилятором (впрочем, кто им сейчас пользуется?)
    Совсем забыл про это, спасибо.

    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    На данный момент я сделал это и ещё несколько изменений (немного изменил принцип работы функции, убрал зависимость от amx_assembly) и хочу выложить всё в виде готового инклуда. Каким образом мне записывать тебя в копирайтах? ("VVWVV"? Настоящее имя?) И ты не против будешь, если я выложу код под лицензией zlib?
    Да, можешь написать "VVWVV". zlib - хорошая лицензия

 

 

Информация о теме

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

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •