segunda-feira, 19 de março de 2012

Aula 06 - Aprofundando-se em Comandos e Chat

Aula 06 - Aprofundando-se em Comandos
Vamos nos aprofundar um pouco mais no assunto "Comandos" (0x334) e iremos conhecer o "Chat", que é o que o player digita (0x333).

Sempre quando mandamos um comando pelo client, o sevidor recebe em forma de packet. O packet é desencriptado pelo servidor e a DLL que a gente usa, lê o packet desencriptado e com uma função, ele interpreta-o. 

A tal função para interpretá-lo é a Commands_Game. Com essa função, podemos criar qualquer tipo de comando. A informação do comando digitado é mandada para o pServer->eCommand. Com a informação do comando digitado, podemos fazer a comparação do comando digitado com um comando que você queria utilizar com strcmp();. - strcmp reference  O comando strcmp compara duas strings e se forem iguais retorna 0.

Depois de compararmos, e ela for zero, temos um bloco de comandos do tamanho que quisermos, fazendo o que quisermos dentro do jogo (desde que você tenha todas as funções para exercer o que quer, poderá fazer qualquer coisa). 

Na aula 4 (quatro) aprendemos como adicionar um novo comando. Agora iremos aprender a pegar parâmetros nele. Como por exemplo: caso eu queira criar o comando /move teríamos também que pegar também as coordenadas, correto? E você tem ideia de como fazer isso?

Para fazermos isso, teremos que usar uma função da biblioteca cstdio.h. A função se chama sscanf();. Com essa, poderemos "escanear" a string (parâmetro 1) de acordo com o formato (parâmetro 2) e registra nos parâmetros adicionais. Nossa string "escaneada" seria o pServer->eValue  os parâmetros adicionais serão o que vamos aprender. Um exemplo é mais fácil para aprendermos.

No caso do comando move teríamos que escanear dois argumentos tipo integer. Ficaria assim:
int x, y;
sscanf(pServer->eValue, "%d %d", &x, &y);

Temos que declarar sempre a quantidade de variáveis que vão ser escaneadas. Se precisamos de 3 argumentos para o comando, temos que declarar para variáveis. É claro que um tipo de variável é diferente de outro. Então, caso você vá precisar pegar um texto nesse escaneamento, você deve declarar variáveis tipo char.

E o que são os %d %d?
São o que vai ser escaneado. Digamos assim: o pServer->eValue (chamarei daqui por diante de eValue apenas) retorna o valor APÓS o comando, ou seja, "/item eValue" e cada %d siginifica o que será armazenado numa variável. Se temos o move, temos então /move 2100 2100. São duas variáveis. No caso, duas %d. Os %d serão armazenados respectivamente. Se o 1° %d tem referência para X, nos parâmetros adicionais teremos que botar o 1° como X.


Lista de tipos de variáveis:

Tipo
                                                          O que é?
Tipo de argumento

%c
Texto simples: lê apenas um caractere. Se o tamanho for maior que um, a variável é armazenado no local especificado passado no argumento numa matriz

char *
%d
Número decimal: Número opcional precedido de + ou -
int *
%e %E %f %g %G
Ponto flutuante (float): Número decimal contendo um ponto decimal, opcionalmente precedido de + ou – e opcionalmente precedido por um e ou E caracteres e um número decimal. Dois exemplos válidos são:  -732.103 e 7.12e4

float *
%o
Octagonal integer
int *
%s
Cadeia de caracteres: Esse tipo de sscanf lê todos os caracteres até encontrar um espaço (espaço são considerados brancos, novas linhas e tabs)

char *
%u
Unsigned decimal integer
unsigned int *
%x %X
Hexadecimal
int *
 (Fonte: cplusplus.com - Traduzido e reescrito por Shepher)

Bom, caso tenha entendido, para criarmos um texto em que captura o nome do personagem, deveremos usar qual tipo de variável? 
Resposta :

Se você respondeu %s, parabéns, você conseguiu entender o que a tabela acima significa!
Você acertou? Parabéns!
E agora, como ficaria no sscanf?
char nick[96]; // ** OBS abaixo
sscanf(pServer->eValue, "%s", nick);
Importante **: Para variáveis tipo char devemos sempre por um valor acima de 48, pois quando uma matriz passa de seu limite, ocorre overflow (sobrecarrega e o servidor fecha). Se botassemos 12, que é o tamanho de um nick, e uma pessoa digitasse por exemplo: VOUCAUSARCRASHNOSEUSERVIDORMANÉ iria causar um crash, porque a variável nick guarda apenas 13 caracteres (contagem de 0~12) e como a digita é maior que isso, causaria overflow.

Bom, com isso podemos já pegar variáveis tipo char e int. E se o nosso caso não for até um espaço em branco, e sim uma vírgula, ou até achar uma nova linha?
Teremos que usar a sequencia [^caractere]; Como funciona?
Caso quisermos até encontrar um sinal de igualdade, fica:
char nick[96]; // Lembre-se da obs acima
sscanf(pServer->eValue, "%[^=]", nick
Sacou? Outro exemplo, agora até uma nova linha:
char nick[96];
sscanf(pServer->eValue, "%[^\n]", nick

Isso fará com que ele carregue até achar uma nova linha!


Tá e aí, tínhamos falado do comando /move não vamos fazê-lo? 
Calma aí. Estávamos aprendendo tudo sobre sscanf antes. Agora vamos partir.

Primeiramente, pensaremos, como sempre! O que um comando /move precisa?
Pegar as coordenadas digitadas
Verificar se foi digitado as duas coordenadas (x e y)
Comparar com os limites do servidor (aula 2) se for menor que os limites e maior que 0, teleporta.

Caso você tenha lido todas as aulas, conseguirá fazer. E a resolução está aqui:


Resolução :

else if(!strncmp(pServer->eCommand, "move")) { int x=0; // Se for 0, quer dizer que não digitou int y=0; // Se for 0, quer dizer que não digitou sscanf(pServer->eValue, "%d %d", &x, &y); if((x == 0) || (y == 0)) { SendClientMsg(clientid, "Coordenadas inválidas"); return; } if((x < 4096 || x > 0 || y < 4095 || y >= 0)) { sprintf(tmp, "Movido para %dx %dy", x, y); SendClientMsg(clientid, tmp); Teleportar(clientid, x, y); return; } }
Bem simples, você não acha?

Chat (0x333)
O tal "chat" é o que é digitado normalmente, e não como um comando. Ou seja, é você falando com outro player. O nome da sua funções é Commands_Say();. Bom, não há muito o que explicar.
O que é escrito vem no pServer->eChat. Se você, por exemplo, usar strcmp e digitar UP TEAM (exemplo da própria DLL) irá comparar apenas o início, e não tudo. E caso for escrito isso, você pode fazer qualquer coisa.

Um exemplo para os novos usuários, é um filtro de palavrões. O que seria? Nada mais do que se você digitar uma palavra feia, você irá fazer com que mostre uma mensagem para o player. Mas não só no início do que foi digitado, mas sim no meio de tudo. Utilizando a função strstr(); que compara a string 2 com a string 1. Se não for encontrado a string 2 na string 1, retorna NULL. 

O código ficaria o seguinte:
char Palavroes[7][10] = {{"porra"}, {"merda"}, {"cacete"}, {"bosta"}, {"vtnc"}, {"viado"}, {"puto"}};
for(int i; i<8;i++)
if(strstr(pServer->eChat, Palavroes[i]) != NULL)
{
SendClientMsg(clientid, "Lave a boca com sabão!");
return;
}
Filtro de palavrões - Shepher



Finalizando
Bom, galera. Esse foi nossa aula mais extensa. E vou fechando por aqui. Fiquem com Deus!

Nenhum comentário:

Postar um comentário