kusehsup

Untitled

Jul 30th, 2026
51
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pawn 8.98 KB | None | 0 0
  1. /*
  2.     ПРОТОТИП: диспетчер внешних команд без центрального switch.
  3.  
  4.     Идея:
  5.     1. Модуль регистрирует handler на конкретный command_id при Init.
  6.     2. Poll читает строки из `external_commands` и зовёт только нужный callback.
  7.     3. Lifecycle (OK / BAD_REQUEST / DELETE / WAIT_RESPONSE) остаётся здесь.
  8.  
  9.     Пример в модуле:
  10.  
  11.         public: FamilyTabletInit()
  12.         {
  13.             ExternalCommands:Register(COMMAND_FML_UPLOAD_LOGO, "FamilyTablet_OnExtUploadLogo");
  14.         }
  15.  
  16.         public: FamilyTablet_OnExtUploadLogo(sql_id, command_type, data_1, data_2, data_3, data_string_1[])
  17.         {
  18.             if (data_1)
  19.                 return COMMAND_STATE_OK; // старый формат — пропускаем
  20.  
  21.             if (isnull(data_string_1))
  22.             {
  23.                 ExternalCommands:SetResponse("Error: Empty logo upload data");
  24.                 return COMMAND_STATE_BAD_REQUEST;
  25.             }
  26.  
  27.             FamilyTablet:UploadLogo(data_string_1);
  28.             return COMMAND_STATE_OK;
  29.         }
  30.  
  31.     Текущий external_commands.pwn этим файлом не заменяется — это набросок архитектуры.
  32. */
  33.  
  34. #if defined _external_commands_dispatch_included
  35.     #endinput
  36. #endif
  37. #define _external_commands_dispatch_included
  38.  
  39. #define ExternalCommands:%0(%1) ExtCmd_%0(%1)
  40.  
  41. #if !defined MAX_EXTERNAL_COMMANDS
  42.     #define MAX_EXTERNAL_COMMANDS 256
  43. #endif
  44.  
  45. #if !defined EXT_CMD_CALLBACK_LEN
  46.     #define EXT_CMD_CALLBACK_LEN 48
  47. #endif
  48.  
  49. // Типы команд
  50. enum
  51. {
  52.     COMMAND_TYPE_EXECUTE = 1,
  53.     COMMAND_TYPE_WAIT_RESPONSE,
  54.     COMMAND_TYPE_PROCESS
  55. }
  56.  
  57. // Статусы ответа
  58. enum
  59. {
  60.     COMMAND_STATE_WAIT_RESPONSE = 1,
  61.     COMMAND_STATE_OK,
  62.     COMMAND_STATE_BAD_REQUEST,
  63.     COMMAND_STATE_NOT_IMPLEMENTED,
  64.     COMMAND_STATE_NOT_FOUND,
  65.     COMMAND_STATE_SERVER_ERROR
  66. }
  67.  
  68. /*
  69.     Сигнатура handler'а (обязательно public):
  70.  
  71.     public: Module_OnExtCmd(sql_id, command_type, data_1, data_2, data_3, data_string_1[])
  72.     {
  73.         // ...
  74.         return COMMAND_STATE_OK;
  75.     }
  76.  
  77.     Возврат = новый state команды.
  78.     Текст ответа пишется через ExternalCommands:SetResponse(...) либо напрямую в FormatData_256.
  79.     Для async (WAIT_RESPONSE): вернуть COMMAND_STATE_WAIT_RESPONSE и позже вызвать ExternalCommands:Finish(...).
  80. */
  81.  
  82. static stock g_ext_cmd_callback[MAX_EXTERNAL_COMMANDS][EXT_CMD_CALLBACK_LEN];
  83.  
  84. // ---------------------------------------------------------------------------
  85. // API регистрации
  86. // ---------------------------------------------------------------------------
  87.  
  88. stock ExternalCommands:Register(command_id, const callback[])
  89. {
  90.     if (!(0 <= command_id < MAX_EXTERNAL_COMMANDS))
  91.     {
  92.         printf("[external-commands] Register failed: command_id %d out of range 0..%d", command_id, MAX_EXTERNAL_COMMANDS - 1);
  93.         return 0;
  94.     }
  95.  
  96.     if (!isnull(g_ext_cmd_callback[command_id]))
  97.     {
  98.         printf("[external-commands] Register overwrite: command_id %d (%s -> %s)", command_id, g_ext_cmd_callback[command_id], callback);
  99.     }
  100.  
  101.     format(g_ext_cmd_callback[command_id], EXT_CMD_CALLBACK_LEN, "%s", callback);
  102.     printf("[external-commands] registered command_id %d -> %s", command_id, callback);
  103.     return 1;
  104. }
  105.  
  106. stock ExternalCommands:Unregister(command_id)
  107. {
  108.     if (!(0 <= command_id < MAX_EXTERNAL_COMMANDS))
  109.         return 0;
  110.  
  111.     g_ext_cmd_callback[command_id][0] = EOS;
  112.     return 1;
  113. }
  114.  
  115. stock bool: ExternalCommands:HasHandler(command_id)
  116. {
  117.     return (0 <= command_id < MAX_EXTERNAL_COMMANDS) && !isnull(g_ext_cmd_callback[command_id]);
  118. }
  119.  
  120. stock ExternalCommands:SetResponse(const response[])
  121. {
  122.     strdel(FormatData_256, 0, sizeof FormatData_256);
  123.     strcat(FormatData_256, response);
  124.     return 1;
  125. }
  126.  
  127. /*
  128.     Завершение async-команды (COMMAND_TYPE_WAIT_RESPONSE),
  129.     когда результат известен позже (например после mysql / Shared_InitLoadData).
  130. */
  131. stock ExternalCommands:Finish(sql_id, command_state, const response[] = "OK")
  132. {
  133.     mysql_format(
  134.         mysql,
  135.         FormatData_512,
  136.         sizeof FormatData_512,
  137.         "UPDATE `external_commands` SET response = '%e', `state` = %d WHERE `id` = %d",
  138.         response,
  139.         command_state,
  140.         sql_id
  141.     );
  142.     mysql_tquery(mysql, FormatData_512);
  143.  
  144.     printf("[external-commands] Finish sql_id=%d state=%d response=%s", sql_id, command_state, response);
  145.     return 1;
  146. }
  147.  
  148. // ---------------------------------------------------------------------------
  149. // Poll / dispatch
  150. // ---------------------------------------------------------------------------
  151.  
  152. stock ExternalCommands:OnTimerProcess()
  153. {
  154.     strdel(FormatData_256, 0, sizeof FormatData_256);
  155.  
  156.     format(
  157.         FormatData_256, sizeof FormatData_256,
  158.         "SELECT *, `command_type` + 0 AS `command_type_int` \
  159.         FROM `external_commands` \
  160.         WHERE `command_type` = %d OR (`command_type` = %d AND `state` = %d)",
  161.         COMMAND_TYPE_EXECUTE,
  162.         COMMAND_TYPE_WAIT_RESPONSE,
  163.         COMMAND_STATE_WAIT_RESPONSE
  164.     );
  165.  
  166.     mysql_tquery(mysql, FormatData_256, "OnUpdateExternalCommands");
  167. }
  168.  
  169. public: OnUpdateExternalCommands()
  170. {
  171.     new rows = cache_num_rows();
  172.  
  173.     for (new idx; idx < rows; idx++)
  174.     {
  175.         new sql_id, command, command_type;
  176.         new data_1, data_2, data_3;
  177.         new data_string_1[256];
  178.  
  179.         cache_get_value_name_int(idx, "id", sql_id);
  180.         cache_get_value_name_int(idx, "command", command);
  181.         cache_get_value_name_int(idx, "command_type_int", command_type);
  182.  
  183.         cache_get_value_name_int(idx, "data_1", data_1);
  184.         cache_get_value_name_int(idx, "data_2", data_2);
  185.         cache_get_value_name_int(idx, "data_3", data_3);
  186.         cache_get_value_name(idx, "data_string_1", data_string_1);
  187.  
  188.         strdel(FormatData_512, 0, sizeof FormatData_512);
  189.         strdel(FormatData_256, 0, sizeof FormatData_256);
  190.         strcat(FormatData_256, "OK");
  191.  
  192.         printf("[external-commands] process sql_id=%d command=%d type=%d", sql_id, command, command_type);
  193.  
  194.         new new_state = ExternalCommands:Dispatch(sql_id, command, command_type, data_1, data_2, data_3, data_string_1);
  195.  
  196.         if (command_type == COMMAND_TYPE_WAIT_RESPONSE)
  197.         {
  198.             // Если handler вернул WAIT_RESPONSE — ответ допишет ExternalCommands:Finish позже.
  199.             if (new_state != COMMAND_STATE_WAIT_RESPONSE)
  200.             {
  201.                 ExternalCommands:Finish(sql_id, new_state, FormatData_256);
  202.             }
  203.         }
  204.         else if (command_type == COMMAND_TYPE_EXECUTE)
  205.         {
  206.             mysql_format(mysql, FormatData_512, sizeof FormatData_512, "DELETE FROM `external_commands` WHERE `id` = %d", sql_id);
  207.             mysql_tquery(mysql, FormatData_512);
  208.         }
  209.     }
  210.  
  211.     return 1;
  212. }
  213.  
  214. stock ExternalCommands:Dispatch(sql_id, command, command_type, data_1, data_2, data_3, data_string_1[])
  215. {
  216.     if (!(0 <= command < MAX_EXTERNAL_COMMANDS) || isnull(g_ext_cmd_callback[command]))
  217.     {
  218.         format(FormatData_256, sizeof FormatData_256, "Error: Unknown command %d", command);
  219.         printf("[external-commands] no handler for command %d", command);
  220.         return COMMAND_STATE_BAD_REQUEST;
  221.     }
  222.  
  223.     /*
  224.         CallLocalFunction:
  225.         i = sql_id
  226.         i = command_type
  227.         i = data_1
  228.         i = data_2
  229.         i = data_3
  230.         s = data_string_1
  231.  
  232.         Возврат callback'а = COMMAND_STATE_*.
  233.     */
  234.     new state = CallLocalFunction(
  235.         g_ext_cmd_callback[command],
  236.         "iiiiis",
  237.         sql_id,
  238.         command_type,
  239.         data_1,
  240.         data_2,
  241.         data_3,
  242.         data_string_1
  243.     );
  244.  
  245.     if (state <= 0)
  246.         state = COMMAND_STATE_OK;
  247.  
  248.     return state;
  249. }
  250.  
  251. stock CleanupWaitingExternalCommands()
  252. {
  253.     strdel(FormatData_256, 0, sizeof FormatData_256);
  254.  
  255.     mysql_format(
  256.         mysql,
  257.         FormatData_256,
  258.         sizeof FormatData_256,
  259.         "DELETE FROM `external_commands` WHERE `command_type` = %i AND `state` != %i",
  260.         COMMAND_TYPE_WAIT_RESPONSE,
  261.         COMMAND_STATE_WAIT_RESPONSE
  262.     );
  263.     mysql_tquery(mysql, FormatData_256);
  264.     return 1;
  265. }
  266.  
  267. // ---------------------------------------------------------------------------
  268. // Пример: как выглядит handler в family_tablet (НЕ подключать отсюда)
  269. // ---------------------------------------------------------------------------
  270.  
  271. #if defined EXT_CMD_DISPATCH_EXAMPLES
  272.  
  273. public: FamilyTablet_OnExtUploadLogo(sql_id, command_type, data_1, data_2, data_3, data_string_1[])
  274. {
  275.     #pragma unused sql_id, command_type, data_2, data_3
  276.  
  277.     // Старый формат от микросервиса (data_1 заполнен) — игнор, ждём новую команду.
  278.     if (data_1)
  279.         return COMMAND_STATE_OK;
  280.  
  281.     if (isnull(data_string_1))
  282.     {
  283.         ExternalCommands:SetResponse("Error: Empty logo upload data");
  284.         return COMMAND_STATE_BAD_REQUEST;
  285.     }
  286.  
  287.     FamilyTablet:UploadLogo(data_string_1);
  288.     return COMMAND_STATE_OK;
  289. }
  290.  
  291. /*
  292.     Пример async (WAIT_RESPONSE), по аналогии с REMOVE_DONATE / Shared load:
  293.  
  294.     public: SomeModule_OnExtRemoveDonate(sql_id, command_type, data_1, data_2, data_3, data_string_1[])
  295.     {
  296.         // data_1 = amount, data_string_1 = name
  297.         Shared_InitPlayerByName(data_string_1, "SomeModule_OnPlayerLoaded", ...);
  298.         return COMMAND_STATE_WAIT_RESPONSE;
  299.     }
  300.  
  301.     public: SomeModule_OnPlayerLoaded(playerid, ...)
  302.     {
  303.         // ... логика ...
  304.         ExternalCommands:Finish(sql_id, COMMAND_STATE_OK, "OK: donate removed");
  305.     }
  306. */
  307.  
  308. #endif
  309.  
Advertisement
Add Comment
Please, Sign In to add comment