Bom alguém teria um sistema de gari estilo o CVR pra estudos, pois estou desenvolvendo o meu perdido como finalizar o sistema. Se alguém tiver e poder mim passa agradeço, pois só queria pegar as posições dos objetos de lixeira e text3dlabel
Bom galera como o titulo já mesmo diz, queria saber como faço para abrir a porta do meu moldem pois mim esquece como faz, pq a ultima vez que fiz isso para deixa o servidor online para outras pessoas já faz 8 anos. Se alguém souber e poder mim ajuda agradeço muito
Opa, estou com um pequeno problema com PlayAudioStreamForPlayer, upei o arquivo .mp3 no dropbox e coloque (dl.dropboxusercontent.com), so que nao funcionou(Nao tocou a musica)...
Olá somos um servidor dedicado ao RP e somos um servidor de RP exclusivo do anime Naruto.
Nosso sistema de RP não é complexo como servidores padrões de RP, nos chamamos nosso estilo de "RP dinâmico".
Para entrar no servidor não é necessário fazer whitelist ou algo do tipo, basta baixar o nosso launcher ou a pasta do jogo e jogar.
Contamos com atendimento aos jogadores em nosso discord através de tickets ou no próprio jogo.
O que possui no servidor?
Vilas
Clãs
Jutsus
Combos de taijutsu (terrestre e aéreo)
Casas
Ranks
Exames (Chunin até o momento)
Missões (NPC)
Missões (Hokage)
Missões RP
Assassinatos
Sequestros e muito mais!
Caso ficou interessado na proposta do servidor, você pode pesquisar nosso nome Naruto World Evolution SAMP no youtube para descobrir mais.
Olá a todos, eu sempre acompanho o SAMPVOICE e sua equipe e sempre tentei adiciona-lo ao 0.3.dl, porém, o problema vem sempre através da parte do client, no caso os arquivos da pasta não se conectam no servidor, dando o erro de que o jogador não está com a versão correta do plugin.
Alguém poderia me ajudar?
(Já vi vários 0.3.DL por ai com o sampvoice, porém, nunca divulgaram)
Citar:- Sincronização de dados;
- Funções Get*;
- Mundos virtuais e processo de "streaming";
- Pool interna de processamento de RPCs.
- Dessincronização.
• Sincronização de dados.
Citar:Todos os dados como posição, arma atual e velocidade vem em pacotes. Ou mais precisamente no caso de UDP, datagramas. Cada datagrama contém uma ou mais mensagens, podendo ser vida, posição atual, entre outros.
Cada pacote é identificado pelos seus primeiros 8 bits (1 byte), que nesse caso é o seu identificador numérico. Existem diferentes tipos de pacotes contendo diferentes mensagens/dados em si, sendo:
ID_PLAYER_SYNC (ID: 207) - Um pacote com este identificador numérico é responsável por atualizações a pé, dos jogadores. Este pacote sempre será enviado APENAS se o jogador estiver a pé, e é enviado a cada um segundo se o jogador não estiver se movendo. A sua estrutura de dados é a seguinte:
lrKey - Seta para esquerda/direita (←→) - Se a tecla atual for seta para esquerda, então lrKey se iguala a KEY_LEFT, caso contrário KEY_RIGHT. 0 se nenhum dos casos.
udKey - Seta para cima/baixo (↑↓) - Se a tecla atual for seta para cima, então lrKey se iguala a KEY_UP, caso contrário KEY_DOWN. 0 se nenhum dos casos.
keys - Um mapa (bitmask) com as teclas pressionadas.
position[3] - Posição atual (X, Y, Z).
quaternion[4] - Quaterniões de rotação (W, X, Y, Z).
health - Vida atual do jogador.
armour - Colete atual do jogador.
weaponId - Arma atual do jogador.
additionalKey - Tecla adicional.
specialAction - Ação especial.
velocity[3] - Velocidade nos eixos X, Y e Z.
surfingOffsets[3] - Offsets de surfing em um veículo - 0.0 em todas as offsets se o jogador não estiver surfando um veículo.
surfingVehicleId - O ID do veículo que o jogador está surfando (0 se nenhum).
animationId - ID da animação atual.
animationFlags - Flags da animação atual.
ID_VEHICLE_SYNC (ID: 200) - Um pacote com este identificador numérico é responsável por atualizações do jogador dentro de um veículo (como motorista). A sua estrutura de dados é a seguinte:
vehicleId - ID de veículo atual.
lrKey - Seta para esquerda/direita (←→) - Se a tecla atual for seta para esquerda, então lrKey se iguala a KEY_LEFT, caso contrário KEY_RIGHT. 0 se nenhum dos casos.
udKey - Seta para cima/baixo (↑↓) - Se a tecla atual for seta para cima, então lrKey se iguala a KEY_UP, caso contrário KEY_DOWN. 0 se nenhum dos casos.
keys - Um mapa (bitmask) com as teclas pressionadas.
quaternion[4] - Quaterniões de rotação do veículo (W, X, Y, Z).
position[3] - Posição atual (X, Y, Z)
velocity[3] - Velocidade nos eixos X, Y e Z.
vehicleHealth - Vida atual do veículo.
playerHealth - Vida do jogador.
armour - Colete do jogador.
weaponId - Arma atual do jogador.
additionalKey - Tecla adicional.
sirenState - Estado da sirene do veículo.
landingGearState - Estado do trem de pouso (dado também utilizado por veículos como empilhadeira para representar o estado do garfo/forquilha).
trailerId - ID de trailer anexado ao veículo (0 se nenhum).
trainSpeed - Velocidade do trem (dado utilizado por trens).
ID_PASSENGER_SYNC (ID: 211) - Um pacote com este identificador numérico é responsável por atualizações do jogador dentro de um veículo (como passageiro). A sua estrutura de dados é a seguinte:
vehicleId - ID de veículo atual. seatId - Número do assento atual. driveBy - 0 se o jogador não estiver em modo de drive-by, caso contrário 1. weaponId - ID de arma atual. additionalKey - Tecla adicional. playerHealth - Vida do jogador. playerArmour - Colete do jogador.
lrKey - Seta para esquerda/direita (←→) - Se a tecla atual for seta para esquerda, então lrKey se iguala a KEY_LEFT, caso contrário KEY_RIGHT. 0 se nenhum dos casos.
udKey - Seta para cima/baixo (↑↓) - Se a tecla atual for seta para cima, então lrKey se iguala a KEY_UP, caso contrário KEY_DOWN. 0 se nenhum dos casos.
keys - Um mapa (bitmask) com as teclas pressionadas.
position[3] - Posição atual do jogador (X, Y, Z).
ID_AIM_SYNC (ID: 203) - Um pacote com este identificador numérico é responsável por atualizações relacionadas com a câmera/mira do jogador. A sua estrutura de dados é a seguinte:
ID_BULLET_SYNC (ID: 206) - Um pacote com este identificador numérico é responsável por atualizações de tiros/balas disparados por um jogador. A sua estrutura de dados é a seguinte:
hitId - ID do veículo, objeto ou jogador que levou o tiro.
origin[3] - Origem da bala.
hitPos[3] - Posição de acerto.
offsets[3] - offsets da bala.
weaponId - ID da arma que a bala pertence.
ID_TRAILER_SYNC (ID: 210) - Um pacote com este identificador numérico é responsável por atualizações de trailers anexados a veículos sendo utilizados por jogadores. A sua estrutura de dados é a seguinte:
trailerId - ID do trailer anexado.
position[3] - Posição atual (X, Y, Z).
quaternion[4] - Quaterniões de rotação do trailer (W, X, Y, Z).
velocity[3] - Velocidade nos eixos X, Y e Z.
angularVelocity[3] - Velocidade angular.
ID_WEAPONS_UPDATE (ID: 204) - Um pacote com este identificador numérico é responsável por atualizações de balas e armas em todas as slots. A sua estrutura de dados é a seguinte:
slot[12] - Slot.
arma0[12] - ID da arma.
balas0[12] - Quantidade de balas.
ID_RCON_COMMAND (ID: 201) - Um pacote com este identificador numérico é responsável por mandar tentativas de comandos rcon para o servidor. Todos os comandos rcons são mandados por esse pacote, até mesmo tentativas de login. A sua estrutura de dados é a seguinte:
text_length - UINT32 com a quantidade de células do texto do comando.
cmdtext - Texto do comando.
• Funções Get*.
Citar:Funções Get* (como GetPlayerWeapon) retornam valores que o servidor já tem, sem nenhuma operação I/O. Esses valores são contidos nos dados dos pacotes de sincronização que jogadores enviam para o servidor. Assim que recebidos os dados, os valores são escritos em variáveis nas estruturas internas do servidor em CPlayerPool, assim, a função retornará o valor já presente em uma das variáveis nas estruturas internas.
Após um pacote de sincronização chegar no servidor e ter seus dados escritos nas estruturas internas, a callback OnPlayerUpdate é chamada. O valor retornado na callback OnPlayerUpdate determina se os dados recebidos pelo servidor vão ser transmitidos com outros jogadores ou não, nesse caso retornando 1 permite que os dados sejam transmitidos com outros jogadores após terem sidos salvos nas estruturas internas do servidor, e retornando 0 impedirá a transmissão desses dados.
• Mundos virtuais e processo de "streaming".
Citar:O servidor controla aquilo que será transmitido com o cliente. Outros jogadores só vão ser criados se você estiver a uma certa distância de suas posições atuais e se o mundo virtual for o mesmo, resultando no processo de stream-in com outros clientes.
No processo de stream-in com jogadores, o servidor manda a chamada de procedimento remoto (RPC - Remote Procedure Call) WorldPlayerAdd para seu cliente, criando os outros jogadores em seu cliente e então permitindo que você receba dados de sincronização desses outros clientes. O mesmo serve para veículos e objetos, o processo de stream-in ocorre quando você chega na distância necessária (geralmente uma distância menor que 200.0 no plano X, Y - O valor pode variar, depende de como você definiu stream_distance em server.cfg, mas por padrão é 200.0) e se estiver no mesmo mundo virtual.
Chegando a uma distância superior à stream_distance, os jogadores/veículos/objetos sofrem stream-out, em que são removidos do seu cliente, logo esses jogadores não terão dados de sincronização mandados para você.
O cliente não controla aquilo que será transmitido consigo mesmo, o servidor é responsável por isso baseado em fatores como posição e mundo virtual. O cliente pode chegar ao ponto de modificar a posição resultando no servidor em fazer o processo de stream-in/out, mas, o cliente nunca poderá alterar o mundo virtual, que é totalmente controlado pelo servidor.
No momento que o mundo virtual muda, todo jogador/objeto/carro que está próximo de você e dentro da distância de streaming e não pertencer ao seu mundo virtual, sofrerá o processo de stream-out, e aquilo que ainda não foi transmitido não será. Você estará limitado a outros jogadores/veículos/objetos que estejam no seu mesmo mundo virtual.
• Pool interna de processamento de RPCs.
Citar:Chamadas de procedimento remoto (RPCs - Remote Procedure Calls) tem o objetivo de executar funções internas no cliente. Quando você muda a ação especial de um jogador com a função SetPlayerSpecialAction, o RPC 88 é mandado para o cliente, tendo o ID da ação especial como parâmetro, e logo uma função interna é chamada no cliente e a ação especial muda de acordo com a providenciada na chamada de procedimento remoto.
Antes de uma chamada de procedimento remoto ser enviada, o servidor verifica se o cliente está mandando atualizações para se certificar que não esteja AFK. Se o cliente estiver AFK, os RPCs são colocados em uma pool de processamento interna, e assim que o cliente voltar, os RPCs são removidos da pool e processados, chegando no cliente.
Vamos considerar o seguinte exemplo:
- O servidor manda uma chamada de procedimento remoto para mudar a ação especial do cliente.
- O cliente está pausado, resultando na adição do RPC, na pool interna de processamento.
- O cliente volta, então o RPC é processado e removido da pool de processamento, chegando no cliente.
- O cliente manda a nova ação especial no pacote de sincronização a pé.
- Os dados do pacote são escritos nas variáveis das estruturas internas do servidor.
- OnPlayerUpdate é chamada, e se ela retornar 1, a atualização será transmitida para jogadores dentro da área de streaming.
- GetPlayerSpecialAction retornará o novo ID de ação especial mantido em uma variável em CPlayerPool->CPlayer[id_do_jogador].
• Dessincronização.
Citar:A dessincronização, também conhecida como "relógio travado" por algumas pessoas, é uma grande causa de falsos-positivos em relação a verificações de anti-cheat.
Um jogador sofrendo dessincronização, ainda manda pacotes de atualização para o servidor e recebe eles, mas não vai reagir a alguns RPCs mandados pelo servidor, como 145 (SetPlayerAmmo) com o intuito de atualizar/mudar alguma informação do cliente.
Como o cliente acaba não reagindo a certos RPCs, o mesmo não passará pelo processo de stream in/out que é feito com RPCs, resultando jogadores/veículos/objetos em não serem removidos, mas vão parar de mandar dados de sincronização, resultando em que o jogador dessincronizado veja todos como se estivessem pausados/AFK.
Fazer a detecção de um jogador dessincronizado não é complicado, leve em consideração que ele ainda manda atualizações para o servidor, logo a callback OnPlayerUpdate continua sendo chamada. Você pode fazer a detecção tentando mudar a munição na slot 0 utilizando SetPlayerAmmo, já que os jogadores não vão perceber a mudança de munição em punhos (id 0) ou soco inglês (id 1), e verificar se há alguma diferença de valores com a última atualização.
Este é um tutorial simples e rápido para você compreender algumas coisas básicas sobre declaração de vars/arrays em Pawn (1) e como economizar memória com isso (2).
Vou passar rápido no tópico 1 (declaração de vars/arrays em Pawn) e me estender no tópico 2.
Coisas que você precisa saber para entender este tópico:
- Basicamente, todas as variáveis declaradas em Pawn (seja new, seja static) alocarão 32 bits (4 bytes ou 4B) p/cell.
- Cells são representadas por colchetes var[0] e bytes por chaves var{0}.
- Todo caractere da table ASCII (inclusive os especiais) compreendem nos decimais que vão de 0 a 255
- 8 bits podem armazenar um número de 0 a 255.
Logo podemos concluir que: 8 bits (1B) são suficientes para armazenar qualquer caractere (inclusive os especiais).
Sendo assim, a maneira mais eficiente de armazenar uma mensagem dentro de uma string não é inserindo um caractere por cell (4B), e sim um caractere por byte, ou seja, 4 caracteres por cell.
Como faremos isso?
Fácil, use ' ! ' antes de declarar uma String.
Ao invés de:
Código PHP:
new String[16] = "Tutorial Maroto";
Use:
Código PHP:
new String[4] = !"Tutorial Maroto";
Isso faz com que você economize 12 Bytes de um total de 16 (75%). Imagine fazer isso para um servidor inteiro, a memória cai muito.
Enfim, tudo tem seus prós e contras. O nome desse tipo de string, de acordo com Pawn, é packed strings (mensagens compactadas), em que, realmente, alocam uma quantia menor de memória, mas não é possível que você use a função 'format' com elas, o que ferra tudo, porque são muito poucas as strings em um servidor que não passem por um format.vou mostrar uma alternativa.
Existe um comando que por aí em GameModes chamado /Cores, que basicamente faz com que uma dialog MSGBOX apareça na tela do player com as 256 cores de veículos, cada cor com seu respectivo número, tipo isso:
Mas pensa bem, se você for armazenar as 3072 caracteres que tem aí dentro (não é um palpite, têm exatos 3072 caracteres), você teria que fazer algo mais ou menos assim:
Código PHP:
if(!strcmp(cmd, "/cores", true)) { new color_list[3072];
Sim, declarar uma string de 3072 cells, quero dizer 12.288bytes, exatos 12 KILOBYTES.
Será se conseguimos diminuir para 3 KB? diminuir em 75%? Não exatamente, mas conseguimos diminuir muito a memória alocada, e é agora que começa o tutorial.
Já que são 3072 caracteres, precisamos de apenas 768 cells (3072/4). Começamos por aí.
Além disso, vamos ter que criar outra array para armazenar a string que será formatada (usaremos 14 cells).
Por último, usaremos uma função.
Código PHP:
if(!strcmp(cmd, "/Cores", true)) { new Result[768]; // Os 3072 caracteres do resultado final new String[14]; // Os 14 caracteres que serão formatados a cada looping for(new i; i != 256; ++i) { // 256 cores format(String, 14, "{%06x}%03i%s", (VehicleColors[i] >>> 8), i, (((i+1) % 16) ? (" ") : ("\n"))); // Não precisa entender isso, apenas que fique claro que estamos usando format strpack(String, String); // compactando as 14 cells em 14 bytes (que fique claro que a array String NÃO diminuiu de tamanho, apenas a informação foi compactada) strpackcat(Result, String); // juntando a array Result com a array String (formatada e depois compactada) } // Depois do processo ter terminado, a array Result estará prontinha para uso ShowPlayerDialog(playerid, DIALOG_NOANS, DIALOG_STYLE_MSGBOX, "Cores", Result, "Ok", ""); return 1; }
a stock strpackcat (que basicamente concatena 2 packed strings, como se fosse strcat só que de packed strings):
Código PHP:
stock strpackcat(dest[], const source[], maxlength=sizeof(dest)) { if(dest{3}) { if(!dest{0}) { print("JBScript [strpackcat] ERROR: The destiny string must be already packed before using this function."); return 0; } } if(source{3}) { if(!source{0}) { print("JBScript [strpackcat] ERROR: The source string must be already packed before using this function."); return 0; } } new dl = strlen(dest), sl = strlen(source); maxlength *= 4; if(maxlength < dl) { print("JBScript [strpackcat] ERROR: The maxlength is lesser then destiny string length."); return 0; } if(maxlength < sl+dl) { sl = maxlength-dl; } for(new i; i != sl; ++i) { dest{dl+i} = source{i}; } return 1; }
Bom, o tutorial é basicamente isso, não tão útil até porque nem sempre criamos uma array assim tão grande. É mais usado nesses casos (raros) mesmo.
Algumas coisas eu não falei durante o tutorial pois não é o foco (já que devem existir outros tutoriais sobre os detalhes que eu deixei de falar, como o uso de 'char', que basicamente retorna o número de cells que são necessárias para armazenar x caracteres, etc).
Como vejo muitas dificuldades nas diferenças entre SetTimer e SetTimerEx, venho aqui trazer um tutorial simples para vocês.
1. De certeza você já ouviu falar daquela expressão: "SetTimer é para todos os jogadores e SetTimerEx é para cada um." Isso está meio que errado. Não é pra isso que foi criado o SetTimer e SetTimerEx.
2. Usamos SetTimer e SetTimerEx quando queremos chamar uma função com atraso, ou seja, por um determinado tempo depois.
3.SetTimer: Sem parametros. SetTimerEx: Com parâmetros.
Explicação:
SetTimer:
Código PHP:
SetTimer(NomeFuncao, Tempo, Repetição);
NomeFuncao: Será o nome de sua função pública. Tempo: O tempo de duração que você quer chamar ela. Repetição: true = repetir sempre, false = apenas uma vez.
NomeFuncao: Será o nome de sua função pública. Tempo: O tempo de duração que você quer chamar ela. Repetição: true = repetir sempre, false = apenas uma vez. formato: tipos de valores. {Float,_}:... : parâmetros (seguindo o formato definido anteriormente).
Até agora, se você é iniciante, você provavelmente está pensando: Eu não entendi nada! Enfim, para isso estou aqui, vou explicar na sua experiência dia-dia.
Vamos imaginar na sua casa: O Microondas, você coloca algo apenas para esquentar, programa ele pra 1 minuto e a contagem começa a descer até chegar ao 0... ( porém o SetTimer(Ex) é incrementando(subindo), e não descendo.
Outro exemplo: você está brincando de esconde-esconde e tem que contar até 10. Quando chegar ao 10, você já pode procurar eles..
Isso é o SetTimer(Ex). Você faz um SetTimer que 'conte' até 10, e quando chegar no 10, irá executar a função de procurar eles.
Agora digamos que você tenha roubado e parou de contar antes dos 10 segundos. Nesse caso, utilizamos o KillTimer parando o SetTimer(Ex)!
Esse é o conceito de SetTimer(Ex)... Ele vai até onde você mandar! Vamos a uns exemplos práticos para entendermos melhor!
Diminuindo tempo pensando em milissegundos.
O tempo do SetTimer(Ex) é feito em milissegundos. Ou Seja:
Código PHP:
1 segundo = 1000 60 segundos = 60000 // [...]
Se você precisa de 2 minutos e 27 segundos por exemplo, sem gerador ou algo do tipo você teria que bater muita cabeça até conseguir. Para facilitar seu trabalho, iniciante, você bota isso no topo do seu GM/FS. Não me lembro do criador do code, mas vou deixar aqui os créditos pro Y_Less. Se alguem souber de quem criou isso, coloca nos comentários
É claro que você não usaria pra dias, semanas, meses, anos, década e muito menos seculos. Para isso usamos GetTimer, mas fica pra outra explicação.
Agora sim! Com esse código definido, fica muito mais fácil trabalharmos! Vamos ver na prática.
Como ficaria o exemplo: "Você está brincando de esconde-esconde e tem que contar até 10. Quando chegar ao 10, você já pode procurar eles.." na prática?
SetTimer
Código PHP:
SetTimer("Contando", segundos(10), false); // Veja como ficou mais fácil definir o tempo. Mas você também pode optar pelo normal: SetTimer("Contando", 10000, false); // Ele cria uma função contando 10 segundos sem repetição!
// Como SetTimer não tem parametro, nunca passamos valor para ela. Sempre utilizaremos ( ).
forward Contando(); public Contando()// Quando completar os 10 segundos, funciona a callback. { print("Contei até 10 segundos ! Agora posso começar a procurar!"); return true; }
Outro exemplo muito conhecido, o sistema de payday:
Código PHP:
public OnGameModeInit() { SetTimer("PayDay", horas(1), true); /* Criamos um Timer com o nome da função PayDay Ele irá contar até 1 hora. / true = ele irá se repetir depois de terminar. ( voltará a funcionar sozinho ).*/ return true; }
forward PayDay(); public PayDay() // Quando completar as 1 hora, funciona a Callback: { for(new i; i < MAX_PLAYERS; ++i) // Loop - Todos os Players { // Mensagem para todos os players. SendClientMessage(i, -1, "Você recebeu o PayDay!"); } return true; }
Realmente, SetTimer poderia ser para todos os players(usando loop). Mas não é isso o conceito dela. Ela vai muito além disso, como utilizar pra sistema de salvamentos ou outras coisas.
Código PHP:
for(new i; i < MAX_PLAYERS; ++i) { SendClientMessage(i, -1, "Você recebeu o PayDay!"); }
Para quem não entendeu isso, procure por tutoriais de Loop. MAX_PLAYERS definido por padrão é 500... Ou seja vai pegar 500 números que são os slots. Até aqui tudo bem. Mas porque trocou no meu SendClientMessage playerid por i ?
playerid = ID DO PLAYER! LEMBREM-SE DISSO!
O Loop, portanto, vai dar 500 voltas... e vai mandar para todos os ID's.
O ID 0 que é o Fulano com Nick A...
Pro ID 1 que é o Fulano com Nick B...
Assim vai até os 500 players(slots).
SetTimerEx
Existem vários exemplos. Mais vou dar apenas dois. Esse, é um simples bloqueio de comando. Depois de usar, ele permitirá que você utilize o comando de novo só depois de 15 segundos.
Código PHP:
new bool:BloquearComando[MAX_PLAYERS char];
get_cmd_noparams(bloquearcomando) // ICMD ( mesma coisa que /bloquearcomando ) { if(BloquearComando{playerid} == true) return SendClientMessage(playerid, -1, "Você já utilizou o comando! Aguarde 15 segundos para utlizar de novo.");
BloquearComando{playerid} = true; SetTimerEx("BloqComando", segundos(15), false, "i", playerid); // Ele irá criar uma função de 15 segundos e não vai ficar se repetindo. // Como eu falei no meu BIZU: playerid carrega o ID do Player. Ele vai levar o ID do player para a função. SendClientMessage(playerid, -1, "Você Utilizou o Comando: /BloquearComando"); return true; }
forward BloqComando(playerid); public BloqComando(playerid) // playerid CARREGA O ID DO PLAYER ! { BloquearComando{playerid} = false; SendClientMessage(playerid, -1, "Já se passou 15 segundos! Agora você já pode utilizar o comando!"); return true; }
Agora que tal um sistema de carregar suas informações simples?
Código PHP:
get_cmd_noparams(minhasinfo) // ICMD ( mesma coisa que /minhasinfo ) { new Idade = 18; new Float:PontosNaEscola = 9.0; // playerid = seu ID SetTimerEx("CarregInfo", 5000, true, "iif", playerid, Idade, PontosNaEscola); return true; }
Temporizadores não são 100% precisos e sim 75%;
Uso de muitos temporizadores aumentará o uso de Memória/CPU.
Lembrando daqueles "i", "iif", etc.. Sugiro ver melhor os tutoriais de Format.
Código:
i Integer.
d Integer.
a Array. The next parameter must be an integer ("i") with the array's size. [CURRENTLY UNUSABLE]
s String. [CURRENTLY UNUSABLE]
f Float.
b Boolean (true/false).