• 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Plugin] mysql_samp 1.4.0 — notas de versão para SA-MP e open.mp
#1
Information 
? mysql_samp 1.4.0

MySQL para SA-MP e open.mp — o mesmo binário nos dois
MySQL for SA-MP and open.mp — the same binary on both


Download · GitHub · Documentação · CHANGELOG

Quote:?? Plugin MySQL em Rust, 79 natives, sem dependência para instalar. Esta versão, em três linhas:
  • Uma mudança de comportamento: o OnQueryError entregava seus cinco argumentos na ordem invertida, desde a 1.0.0. Se você trata esse forward, confira o handler.
  • Uma correção de estabilidade: uma rajada de queries num único tick podia abortar o servidor e travar a fila de callbacks permanentemente.
  • Uma novidade: MYSQL_SYNC faz uma chamada específica esperar o banco, sem callback.

?? Nenhuma native foi removida ou renomeada e nenhuma assinatura mudou: gamemode de 1.3.0 compila como está.

?? MySQL plugin in Rust, 79 natives, nothing to install. One behaviour change (OnQueryError delivered its five arguments reversed since 1.0.0 — check your handler), one stability fix (a burst of queries in one tick could abort the server and stall the callback queue for good), one new feature (MYSQL_SYNC makes a single call wait on the database, with no callback). Nothing removed, renamed or re-signed: 1.3.0 gamemodes compile unchanged.



⚠️ Mudança de comportamento: OnQueryError
Behaviour change: OnQueryError

?? O forward é declarado assim:
?? The forward is declared as:

Code:
forward OnQueryError(errorid, error[], callback[], query[], connId);

?? Até a 1.3.0 o gamemode recebia os campos ao contrário: o id da conexão chegava como errorid, o texto da query como error, a mensagem de erro como query e o código do MySQL como connId.

?? A causa estava no despacho. O exec_public! empilha os argumentos ao contrário — é a convenção da pilha do AMX — logo a ordem escrita na chamada já é a ordem que o public recebe. O código os listava de trás para frente, como se tivesse de inverter à mão.

?? A inversão era consistente, então um handler podia parecer funcionar há bastante tempo lendo o id da conexão como código de erro. Presente desde a 1.0.0 — se você trata esse forward, é o primeiro lugar a olhar depois de atualizar.

?? Up to 1.3.0 the fields arrived reversed: connection id as errorid, query text as error, error message as query, MySQL code as connId. exec_public! pushes arguments in reverse (the AMX stack convention), so the order written at the call site is already the order the public receives; the code listed them backwards as if it had to invert them by hand. Consistent enough that a handler could appear to work for a long time. Present since 1.0.0.

?? E ao atualizar / ?? And when updating
  • ?? Troque o binário e os includes. O release traz .so, .dll, mysql_samp.inc e mysql_samp_omp.inc. Sem o include novo não existem MYSQL_SYNC nem as natives novas — e include velho com binário novo é o erro mais comum numa atualização. O MYSQL_SAMP_VERSION do include serve justamente para comparar na subida / ?? replace the binary and the includes; MYSQL_SAMP_VERSION exists to catch a stale one
  • ?? Binário de 32 bits, como o servidor. SA-MP em plugins/; open.mp nativo em components/, sem mexer no config.json / ?? 32-bit, like the server
  • ?? Fora o handler do OnQueryError, nada mais a fazer / ?? apart from the OnQueryError handler, nothing else to do



? Correções
Fixed

?? Uma rajada de queries matava o servidor — e podia travar a fila para sempre
?? Submeter 2000 queries num tick abortava o processo com memory allocation of 4194304 bytes failed. A causa é específica desta arquitetura: uma thread por query, com a pilha padrão de 8 MiB, dentro de um servidor 32 bits. Algumas centenas de workers vivos esgotam o espaço de endereçamento, e a próxima alocação que falha leva o processo junto. Foi achado por teste de força bruta, não por relato.

?? Eram dois problemas, os dois corrigidos:
  • Pilha de 256 KiB por worker — um worker abre conexão, roda um statement e monta um cache; não recursa. O teto medido saiu de cerca de 1700 para mais de 9000 workers
  • Thread recusada pelo sistema não é mais pânico — e esta parte era pior que o crash: a submissão se perdia junto com o número de sequência, de modo que toda callback ordenada atrás dela esperaria para sempre. Agora é um resultado com erro comum: a sequência é contabilizada, o OnQueryError dispara e a native devolve false
?? Medido depois da correção: 9000 queries num tick, 9000 callbacks entregues, ordem intacta, nada preso no caminho.
?? 2000 queries in one tick aborted the process — one thread per query, 8 MiB default stack, 32-bit server. Workers now get a 256 KiB stack (ceiling moved from ~1700 to past 9000), and a thread the OS refuses is now an ordinary failed result instead of a panic that lost the sequence number and stalled every ordered callback behind it forever.

?? As natives de submissão passaram a dizer a verdade
?? mysql_query, mysql_pquery, mysql_query_file, mysql_stmt_execute, mysql_stmt_pexecute, mysql_transaction_execute e as natives CRUD do ORM devolviam true sempre que o id da conexão era válido. Agora devolvem o que a submissão realmente fez.
?? They returned true whenever the connection id was valid; they now return what the submission actually did.

?? Pedir TLS em host local rodava em texto claro
?? MYSQL_OPT_SSL = 1 contra 127.0.0.1 reportava sucesso enquanto o cipher voltava vazio. O driver tem prefer_socket ligado por padrão e, depois do handshake, move uma conexão de loopback para o socket unix do servidor — onde ela não protege nada. As duas condições se encontravam na configuração local mais comum que existe. Pedir TLS agora desliga essa otimização; sem TLS ela continua, que é onde ajuda. Verificado contra MariaDB 11.8: TLS_AES_256_GCM_SHA384 onde antes havia string vazia.
?? MYSQL_OPT_SSL = 1 against 127.0.0.1 reported success with an empty cipher: the driver moves a loopback connection onto the unix socket after the handshake, where it secures nothing. Asking for TLS now switches that off.

?? E ainda / ?? And also
  • ?? Erro do driver chegava ao Pawn com entranha de Rust — tabela faltando vinha como MySqlError { ERROR 1146 (42S02): Table 'db.t' doesn't exist }. Agora passa a mensagem do servidor, sozinha / ?? Rust internals no longer wrap the server's own message
  • ?? mysql_query_file falhava sem avisar — arquivo ilegível ou sem statements era só uma linha de log, que a forma sem callback (a recomendada para schema) nunca vê. Agora dispara OnQueryError, com o caminho no lugar da query / ?? now fires OnQueryError, naming the file
  • ?? rustls 0.23.45 (RUSTSEC-2026-0285) — mensagens de handshake TLS 1.3 eram aceitas atravessando limites de nível de criptografia. O transcript segue autenticado, então ninguém na rede altera ou completa um handshake; o efeito prático é um par conseguir mandar em texto claro o que deveria ir cifrado / ?? TLS 1.3 handshake messages accepted across encryption level boundaries



? MYSQL_SYNC: bloquear quando a chamada pede
New: MYSQL_SYNC

?? Até a 1.3.0 não havia caminho bloqueante na API, de propósito. O problema é que existe trabalho que não é sobre desempenho: criar tabela no OnGameModeInit, ler uma configuração que a linha seguinte precisa, migrar schema. Exigir callback para isso é cerimônia, e a resposta para "como eu leio isto agora?" não podia ser "não leia".

?? Up to 1.3.0 there was no blocking path in the API, by design. But some work isn't about performance: creating tables in OnGameModeInit, reading a setting the next line needs, migrating schema.

Code:
mysql_query(g_mysql, "SELECT COUNT(*) AS n FROM contas", MYSQL_SYNC); new total = cache_get_value_name_int(0, "n");  // o resultado já está aqui

?? Funciona em mysql_query, mysql_pquery e mysql_query_file — onde vive o trabalho de schema na subida do servidor.
?? Supported on mysql_query, mysql_pquery and mysql_query_file.

Por que é um valor e não uma native nova
?? Porque uma native chamada mysql_query_sync acaba dentro do OnPlayerUpdate. Um valor escrito no lugar do callback põe a intenção onde o custo é pago: quem lê a linha vê que ela espera. E não podia ser um argumento novo — o Pawn não aceita parâmetro depois de {Float,_}:..., e colocá-lo antes do callback quebraria toda chamada existente.
?? A native called mysql_query_sync ends up inside OnPlayerUpdate. A value at the call site states the intent where the cost is paid. It couldn't be a new argument either: Pawn refuses any parameter after {Float,_}:....

As três regras que mantêm isso honesto
  • ?? Recusado dentro de callback — lá "o cache atual" já significa o do próprio callback; um resultado bloqueante ou o encobriria ou ficaria ilegível. A recusa vai para o log, não passa calada / ?? refused inside a callback, out loud
  • ?? Liberado no tick seguinte — a mesma garantia que o caminho assíncrono ganha do callback retornar. Nada para liberar à mão / ?? freed at the next server tick, nothing to free by hand
  • ?? Recusado nas natives que não podem honrá-lo — prepared statements, transações, ORM e hash de senha. Aceito em silêncio, o valor passaria por nome de callback que nunca dispara, e a chamada viraria fire-and-forget sem você saber / ?? refused on every native that cannot honour it — otherwise the call would silently become fire-and-forget



⚖️ Como usar assíncrono e síncrono
Using async and sync

?? A regra em uma frase: se um jogador está esperando, é assíncrono; se o servidor ainda está subindo, pode ser síncrono.
?? One sentence: if a player is waiting, async. If the server is still starting up, sync is fine.

1. Assíncrono com callback — o caso normal

Code:
mysql_query(g_mysql, "SELECT id, nome FROM contas WHERE nome = 'x'", "OnContaCarregada", "d", playerid); forward OnContaCarregada(playerid); public OnContaCarregada(playerid) {     if (!cache_get_row_count()) return 0;  // conta não existe     new nome[MAX_PLAYER_NAME];     cache_get_value_name(0, "nome", nome);     printf("conta de %s carregada", nome);     return 1; }

2. Assíncrono sem callback — quando o resultado não interessa
?? O callback sempre foi opcional, mas isso estava num parágrafo no pé de uma página enquanto todos os exemplos sugeriam o contrário — daí a reclamação de que o plugin "obriga a escrever callback para tudo". Um UPDATE cujo resultado você não vai ler é uma linha:
?? The callback was always optional; the docs buried it. An UPDATE whose result you won't read is one line:

Code:
mysql_query(g_mysql, "UPDATE contas SET online = 0 WHERE id = 7");

3. Síncrono — trabalho de subida

Code:
public OnGameModeInit() {     g_mysql = mysql_connect_file();                      // credenciais fora do código     mysql_query_file(g_mysql, "schema.sql", MYSQL_SYNC); // schema pronto ANTES de seguir     mysql_query(g_mysql, "SELECT valor FROM config WHERE chave = 'preco'", MYSQL_SYNC);     g_preco = cache_get_value_name_int(0, "valor");      // já disponível aqui     return 1; }

4. FIFO ordena callbacks, não execução — a armadilha
?? Isto merece destaque porque a documentação antiga recomendava errado. O mysql_query entrega os callbacks na ordem em que você submeteu, mas cada statement roda na sua própria conexão, ao mesmo tempo. Um CREATE TABLE seguido de um INSERT corre junto, e o insert falha. Quem serializa de verdade é o mysql_query_file (arquivo inteiro, em ordem) e a transação (lote atômico) — ou o MYSQL_SYNC, que simplesmente espera.
?? mysql_query dispatches callbacks in submission order, but each statement runs on its own connection, concurrently: a CREATE TABLE followed by an INSERT races. What actually serialises is mysql_query_file, a transaction, or MYSQL_SYNC.

5. E o mysql_pquery?
?? Entrega assim que cada uma termina, sem ordem garantida — bom para muitas queries independentes. Com MYSQL_SYNC ele é igual ao mysql_query: uma chamada que espera não tem com o que rodar em paralelo.
?? Delivers as each finishes, no ordering. With MYSQL_SYNC it's identical to the FIFO native.



? Outras novidades
Also new in 1.4.0

Diagnóstico de TLS
?? mysql_tls_active(connId) e mysql_tls_cipher(connId, dest[], max_len) dizem no que o handshake realmente terminou, não o que o MYSQL_OPT_SSL pediu. Os dois leem um valor capturado quando a conexão abriu, então nenhum custa uma query. Sessão sem criptografia devolve false e cipher vazio — essa é a resposta, e o retorno separa isso de uma conexão que não existe.
?? What the handshake actually settled on, rather than what MYSQL_OPT_SSL asked for. Both read a value captured when the connection opened, so neither costs a query.

Code:
if (!mysql_tls_active(g_mysql)) print("[MySQL] atenção: conexão em texto claro"); else {     new cipher[64];     mysql_tls_cipher(g_mysql, cipher, sizeof(cipher));     printf("[MySQL] TLS: %s", cipher);          // ex.: TLS_AES_256_GCM_SHA384 }

Os tetos de memória deixaram de ser fixos
?? mysql_limit_set e mysql_limit_get mexem em três limites antes cravados no código: MYSQL_LIMIT_SAVED_CACHES (1024), MYSQL_LIMIT_RESULT_ROWS (100000) e MYSQL_LIMIT_ORM_STRING_LEN (4096). Quem precisa de mais, aumenta; máquina apertada, diminui. São globais e não por conexão, porque a memória protegida é a do servidor. O valor 0 é recusado em vez de ser lido como "ilimitado" — ilimitado é justamente o estado que esses limites existem para evitar — e abaixar um teto não joga fora o que já está guardado.
?? Three caps that used to be hardcoded. Global, not per connection. 0 is refused rather than read as "unlimited", and lowering a cap keeps what is already held.

Code:
mysql_limit_set(MYSQL_LIMIT_RESULT_ROWS, 250000);  // relatório grande printf("caches guardados: %d", mysql_limit_get(MYSQL_LIMIT_SAVED_CACHES));

${VARIÁVEL} no arquivo de conexão
?? O mysql_connect_file já tirava a senha do gamemode; agora o arquivo pode citar o segredo em vez de guardá-lo, e uma cópia roubada não vale nada por si:
?? The file can now name the secret instead of holding it:

Code:
host = 127.0.0.1 user = samp password = ${MYSQL_PASSWORD} database = meu_banco

?? Nome não definido expande para vazio e diz no log qual faltou — mandar o literal ${MYSQL_PASSWORD} para o servidor falharia como "access denied" e mandaria você procurar no lugar errado. Só ${...} é referência, então senha com sinal de dólar continua funcionando.
?? An unset name expands to nothing and logs which one was missing. Only ${...} is a reference, so a password with a dollar sign still works.

IPv6
?? É suportado, entre colchetes: [::1] é interpretado e validado como endereço. Sem colchetes vira nome de host — normalmente ainda conecta, mas não valida nada.
?? Supported, in brackets: [::1] is parsed and validated as an address; without brackets it's taken as a host name.



⚙️ Por dentro: o SDK e o que ele destravou
Under the hood

?? O plugin roda sobre o rust-samp, o SDK em Rust para SA-MP e open.mp, que aqui vai para a 3.6.0 e sai de uma tag do git para o crates.io. A mudança já conserta uma esquisitice: a tag v3.5.0 declarava version = "3.4.0" no próprio manifesto, então o lockfile registrava 3.4.0 apontando para a tag v3.5.0.

?? O que a 3.6.0 trouxe de útil aqui foi uma rota que faltava. Quando uma query termina numa worker thread, o resultado precisa aterrissar no estado do plugin — e até a 3.5.0 não havia caminho do SDK para isso, limitação que o plugin contornava com canal próprio. O post_with entrega o plugin ao job, e com ele os canais de resultado do plugin deixaram de existir, tanto na query quanto no hash de senha.

?? Não é ganho de desempenho, e eu não vou vender como se fosse. O SDK drena essa fila imediatamente antes do on_tick, então o despacho continua acontecendo no on_tick e nada chega um tick depois do que chegava. O ganho é de arquitetura: menos código próprio fazendo o que o SDK passou a fazer, num ponto onde errar custa caro. Os ganhos de robustez que você sente nesta versão — a rajada de 9000 queries — são do plugin, não do SDK.
?? It is not a performance win and I won't sell it as one. The SDK drains that queue immediately before on_tick, so dispatch still happens there and nothing arrives a tick later than it used to. The gain is architectural. The robustness wins you feel in this release are the plugin's, not the SDK's.

?? Um detalhe de projeto que não é questão de gosto: entregar o resultado e despachar o callback tiveram de ficar em dois passos. Chamar o Pawn de dentro do job deixaria um public reentrar numa native e pegar um segundo empréstimo mutável do mesmo plugin, que é instantaneamente um bug de memória. Então o job só bufferiza, e quem invoca callback é o on_tick, que detém esse empréstimo legitimamente. A ordem FIFO continua sendo nossa, no buffer de sequência, porque os jobs terminam na ordem em que o banco responde, não na que você pediu.
?? Handing the result over and dispatching the callback had to be two steps: calling Pawn from inside the job would let a public re-enter a native and take a second mutable borrow of the plugin. The job only buffers; on_tick dispatches.

Gargalo antigo que já não é seu problema
?? Vindo de outro plugin MySQL você provavelmente aprendeu a chamar mysql_tick() num timer. Aqui não é necessário desde a 3.0.0 do SDK: a fila é bombeada a cada tick, sozinha. A native continua existindo só por compatibilidade — e ela não libera o resultado do MYSQL_SYNC, que sai no tick seguinte de todo jeito.
?? Coming from another MySQL plugin you probably learned to call mysql_tick() on a timer. Not needed here since SDK 3.0.0: the queue is pumped every tick automatically.



? A referência da API agora se escreve sozinha
The API reference generates itself

?? A referência da API era uma tabela escrita à mão com cada native, seu tipo e uma descrição — exatamente a informação que o include já carrega ao lado de cada declaração, mantida em sincronia a dedo. Agora ela é gerada do include a cada build: natives, o forward, as constantes documentadas e os enums com seus valores, cada um com assinatura, apelido open.mp, parâmetros e retorno. A cópia manual foi apagada, e a URL é a mesma.

?? O gerador lê dois formatos de comentário: o JavaDoc (@param, @return), que é o que estes includes usam, e o pawndoc (<summary>, <param name="">, <returns>), que é o que você encontra em includes de terceiros. E tem autoteste: se uma native entrar sem bloco de documentação, o build da doc falha em vez de publicar uma linha vazia.

?? Esses mesmos comentários chegam ao hover do seu editor — na extensão PawnPro para VS Code, por exemplo, você passa o mouse na native e lê o que cada parâmetro espera sem abrir o .inc. Nesta versão as três natives que aceitam MYSQL_SYNC carregam o exemplo bloqueante no próprio comentário, então o jeito de fazer uma chamada esperar aparece onde a native é documentada, não só na página de queries.

?? The API reference is now generated from the includes on every docs build — natives, forward, documented constants and enums with their values. Two comment formats are read: JavaDoc, used here, and pawndoc. A native without a doc block fails the docs build. The same comments reach your editor's hover — e.g. the PawnPro VS Code extension.



Quote:?? Prepared statement continua sendo a via segura. O mysql_format e o ORM interpolam texto e dependem do escaping; o mysql_stmt_* manda os valores pelo protocolo binário e não tem esse risco. Para qualquer coisa que carregue o que o jogador digitou, prefira statement — isso não mudou nesta versão e não vai mudar.
?? Prepared statements are still the safe path for anything carrying player input.

? Links
  • Download da 1.4.0 — ?? .so, .dll e os dois includes / ?? binaries and both includes
  • Documentação — ?? conexão, queries, cache, ORM, opções e segurança / ?? connection, queries, cache, ORM, options, security
  • Referência da API — ?? as 79 natives, geradas do include / ?? all 79 natives, generated from the include
  • Guia de migração — ?? para quem vem de outro plugin MySQL / ?? coming from another MySQL plugin
  • CHANGELOG completo — ?? com o que não caberia aqui / ?? the full list

?? Projeto independente, mantido por uma pessoa. Não é afiliado, endossado nem patrocinado pelo SA-MP ou pelo open.mp. "SA-MP", "open.mp" e "MySQL" pertencem a seus respectivos donos e são citados apenas para descrever compatibilidade.
?? Independent project, maintained by one person. Not affiliated with or endorsed by SA-MP or open.mp.


?? Dúvidas, sugestões e relatos de bug são bem-vindos — aqui no tópico ou nas issues.
?? Questions, suggestions and bug reports are welcome — here or on GitHub issues.


⭐ github.com/NullSablex/mysql_samp
  Reply