quinta-feira, 10 de dezembro de 2009

Seam, Richfaces e Ajax: Conversação para ligar tudo isso.

Desde que comecei a utilizar o Seam sempre fiquei um tanto quanto decepcionado por não conseguir utilizar Ajax para fazer interações básicas com o usuário, tal como o usuário clica em um botão para adicionar algo (uma pessoa) a uma lista, abre-se então uma janela com uma listagem de pessoas que ele pode selecionar e através de um filtro ele pode digitar o nome desta pessoa para limitar o número de resultados. Ao encontrar a pessoa desejada o usuário a seleciona e esta é adicionada a uma lista do sistema que representa algo.

A figura acima mostra um caso típico deste tipo de interação. Este tipo de interação torna muito mais prático a utilização do sistema por parte do usuário, já que o tempo de resposta de cada requisição do usuário é menor.

A pouco tempo atrás comecei a escrever uma nova aplicação onde trabalho, criando um projeto war com o Seam 2.2.0.G.A, o que já me deixou bastante motivado, pois dá primeira vez que tentei criar um projeto desta forma não obtive sucesso, pois tentei utilizar os plugins do Eclipse para criar o projeto, sendo que este simplesmente não fazia deploy das alterações que eu realizava. Criei utilizando o seam-gen e depois importei o projeto no Eclipse e tudo fucionou como esperado, fazendo hot-deploy das classes, o que é um grande passo no desenvolvimento de aplicações para web utilizando Java.

Estava decidido que neste projeto iria utilizar Ajax de qualquer maneira. Após dois dias (infelizmente não trabalho só com desenvolvimento) testando diferentes maneiras de chamar meus componentes descobri o santo graal :D.

Mas vamos começar do começo, pois acredito que o Richfaces tenha problemas mesmo e que a forma como o utilizei apenas contornoram tal problema, mas até hoje não sei por quê. Entrei em contato com os desenvolvedores, mas até hoje não obtive resposta porque tecnicamente a aplicação não se comportava como eu esperava, sendo assim, gostaria de deixar o caminho que percorri, quem sabe alguma alma me explique por que não obtive sucesso inicialmente.

Bem, chega de enrolação e vamos para a prática. Criei um componente de listagem que estende a classe EntityQuery do framework Seam. Na minha janela de busca de pessoas liguei o "Nome" no "Filtro de Busca" ao nome de um campo que é utilizado no meu componente para restringir os resultados da pesquisa. Cada vez que uma letra era digitada na caixa de busca a listagem era re-renderizada. Isto funcionou muito bem, filtro feito!

O problema apareceu quando selecionei uma pessoa pela primeira vez. Quando renderizava a lista de pessoas, o link Selecionar chamava um método para adicionar a pessoa ao sistema passando o objeto que representava a pessoa como parâmetro. O problema é que na aplicação o parâmetro passado era simplesmente null. Já havia visto este problema, quando um colega estava brincando com uma solução parecida.

Comecei então a brincar com o escopo do meu componente. Quando coloquei ele no escopo de página o parâmetro começou a ser passado, porém quando utilizava o filtro e a listagem era re-renderizada o parâmetro que era passado era a referência para o objeto que representava a pessoa que estava na listagem logo que esta abria, ou seja, sem filtro nenhum.

Deve existir alguma explicação para este comportamento, porém não sei explicar qual é, apenas posso dizer que ao colocar a listagem no scope de conversação consegui resolver meus problemas, tudo funcionou perfeitamente, conseguia filtrar os resultados e clicar em um deles e o parâmetro passado a minha função era o objeto correspondente a pessoa a qual eu havia clicado, bingo! Ops, cedo demais para ficar tão feliz :(

Ao digitar muito rapidamente no filtro de busca o Seam gerava um erro: The conversation ended, timed out or was processing another reques. A solução foi bem simples, demorei apenas para encontrá-la: utilizar o componente Queue do Richfaces. Este faz com que as requisições Ajax sejam enfileiradas no browser e submetida apenas quando a requisição atual retornar. Isto evita que chegue uma requisição no servidor enquanto ele já está processando outra requisição durante uma conversação.

Só não fiquei satisfeito de colocar meu componente de listagem no escope de conversação por padrão. Para evitar isso criei um componente que cria e desinjeta tal componente no scope de conversação quando necessário.

Pronto, agora sou um programador feliz, posso utilizar Ajax na minha aplicação com Seam!

quarta-feira, 21 de outubro de 2009

GNOME 3.0: GNOME-Shell e GNOME Zeitgeist

O GNOME está se aproximando da sua versão 3.0. Os carros chefes para esta nova versão são dois softwares: GNOME-Shell e GNOME Zeitgest.

Ambos alteram a forma como se interage com o desktop e com os arquivos. O GNOME-Shell substitui o gerenciador de janelas, o painel e o lançador de aplicações atuais do GNOME, criando um ambiente desktop orientado a atividades com uma interface bem diferente da atual, afinal com este software o GNOME entra finalmente para era dos desktops 3D. Com certeza esta mudança trará muito estranheza para os usuários, porém um fato que gostei bastante foi que este lançamento não "quebrou" o desktop, pelo menos não por enquanto. Algumas distribuições simplesmente param de distribuir software antigo, por exemplo, na última versão do Kubuntu é difícil instalar algumas aplicações antigas do KDE, simplesmente porque a distribuição parou de empacotar as versões mais antigas de bibliotecas. Um grande prejuízo, principalmente no caso do KDE, onde muitas aplicações ainda não foram portadas para o KDE 4.

Alguns vídeos mostrando diversas funcionalidades do GNOME-Shell pode ser encontrado na página do projeto: http://live.gnome.org/GnomeShell/Screencasts

O GNOME Zeitgeist modifica como você vê os arquivos no computador adotando uma linha cronológica, ou seja, se você quiser ver quais arquivos acessou na semana passada é possível. Este é um software que ainda não testei, mas com certeza ainda teremos a possibilidade de ver nossos arquivos em forma de árvore, se não, ficarei muito desapontado, pois não acho que poder enxergar os arquivos apenas de forma cronológica seja a melhor solução.

O Zeitgeist também indexa páginas visitadas e outras fontes de informações. A ambição do projeto é interessante, mas não sei se irá vingar. Sempre que tentei utilizar indexadores, tipo tracker ou beagle, sempre achei que eles consumiam muito processamento e memória, além disso utilizava-os muito pouco. Sendo que as funcionalidades prometidas por tais software eu conseguia obter me organizando. Espero que o Zeitgeist me faça mudar de idéia, pois nada com um software diferente para dar um novo ar a uma mesma idéia!

sexta-feira, 4 de setembro de 2009

Mate o IE6

A algum tempo diversos sites passaram a apoiar a campanha "Mate o IE6". Basta você fazer uma busca por este tema no Google e vai encontrar diversas páginas.

Bem, porque estou fazendo este post? Acabei de olhar as estatística do blog e o IE6 subiu para o segundo lugar nos browser mais usados e provavelmente muito em breve será o primeiro, sendo assim, eu peço aos visitantes que passam por aqui: considerem atualizar seus browsers!

Existem apenas vantagens em se fazer isso: browsers mais seguros, modernos e compatíveis com padrões. Este último item pode não ser muito relevante para você, mas pode ter certeza, vai melhorar cada vez mais a vida dos programadores web.

Agora me diga uma coisa, quantos serviços web você usa hoje em dia? Pode ter certeza que esse número de serviços só vai aumentar, então faça um favor para os desenvolvedores poderem concentrar-se mais nos serviços e menos nos problemas do IE6: mate-o!

quinta-feira, 16 de julho de 2009

Formatar código-fonte para postagem em blogs

No meu último post sofri para formatar o código do meu bash script. Depois de algum tempo tentando deixá-lo com uma aparência razoável pensei que deveria ter alguma coisa pronta. Depois de procurar por algum tempo, encontrei: http://formatmysourcecode.blogspot.com/

Perfeito!!!

Trabalho repetitivo? Bash neles :P

Tenho uma base de dados que contém dados de trabalhos enviados a um evento. Além disso, possuo em um diretório os trabalhos em .rtf.

O que preciso fazer? Converter cada um dos trabalhos para .pdf e inserir no cabeçalho uma imagem do evento com seu ISSN. Além disso, a partir da área, título do trabalho, palavras-chave e autores, preciso gerar uma base de dados em um formato criado por nós para fazer buscas através de javascript nos anais do evento. Para isso, fiz um script que cuida dessas duas coisas.

Como fazia tempo que não mexia com bash script, estava meio enferrujado, mas nada que uma pesquisa no Google não ajude.

Consultei os dados do banco usando SQL e exportei o resultado para um arquivo CSV delimitado por TABs.

Aproveitei a estrutura para ler arquivos apresentadas por: http://bash.cyberciti.biz/file-management/read-a-file-line-by-line/

Além disso, para padronizar o nome dos arquivos, converti todas as informações para maiúsculo, sendo que em alguns lugares precisei retirar o acento das palavras, mas mais uma vez tive uma ajudinha: http://ubuntuforums.org/showthread.php?p=6641997

Porém a conversão das letras com acento não funcionou. Poderia ter aberto o arquivo no OpenOffice e convertido, porém acreditei que existiria um jeito de fazer isso no terminal e depois de muito procurar encontrei a solução: http://wjd.nu/notes/2009. Também vou transcrevê-la para não perdé-la, pois o site não dá nenhuma link direto a ela:

2009-01-26 - unicodeencodeerror / python redirect pipe

Python has excellent Unicode support. Most of the time it just works. However, when you want to redirect non-ASCII characters to a different program through shell redirection, you will run into a UnicodeEncodeError.
See the following example:
$ locale | grep LC_CTYPE
LC_CTYPE="en_US.UTF-8"
$ python -c 'print "\u20ac 3.50"'
\u20ac 3.50
$ python -c 'print u"\u20ac 3.50"'
€ 3.50
$ python -c 'print u"\u20ac 3.50"' | cat
Traceback (most recent call last):
File "", line 1, in 
UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' in position 0: ordinal not in range(128)
Python calls something like isatty(3) ("is a TTY?") on the standard out file descriptor, and when that returns 0 (false) it forces stdout to use the ascii codec. This can be quite annoying if you want to use other shell tools (awk(1), grep(1), sed(1), tr(1) etc..) to quickly search through or modify the output stream.
The solution is to replace the stdout object with one that doesn't assume that a non-terminal only likes ASCII. Add the following to your python code:
import codecs, locale, sys
sys.stdout = codecs.getwriter(locale.getdefaultlocale()[1])(sys.stdout, 'replace')
It's not real pretty, but it works :-)
$ python -c 'import codecs, locale, sys
> sys.stdout = codecs.getwriter(locale.getdefaultlocale()[1])(sys.stdout, "replace")
> print u"\u20ac 3.50"' | cat
€ 3.50

Escrevi/copiei uma macro n{d}o OpenOffice para converter os arquivo .rtf para .pdf e adicionar o cabeçalho com as informações do evento (esta parte me tomou bastante tempo, pois apesar de existir bastante informação de como modificar o cabeçalho e como inserir uma imagem através de macros, esta parte não funcionou no OOo 3.0 disponibilizado pelo GSB para o Slackware, quer dizer, o programa travava 99,9% das vezes e até eu perceber isso, achei que estava fazendo alguma coisa errada. No OOo 2.4 também disponiblizado pelo GSB o programa trava 0.01%. Ainda vou tentar descobrir porque!): http://www.oooforum.org/forum/viewtopic.phtml?t=3772

Sub test( cArg )
Print "|"+cArg+"|"
End Sub

Sub ConvertWordToPDF( cFile )
cURL = ConvertToURL( cFile )

' Open the document.
' Just blindly assume that the document is of a type that OOo will
' correctly recognize and open -- without specifying an import filter.

oDoc = StarDesktop.loadComponentFromURL( cURL, "_blank", 0, Array(MakePropertyValue( "Hidden", True )))

oGraph = oDoc.createInstance("com.sun.star.text.GraphicObject")
With oGraph
.GraphicURL = "file:///home/cerodrigues/kadu.jpg"
.AnchorType = com.sun.star.text.TextContentAnchorType.AS_CHARACTER
.Width = 6000
.Height = 6000
End With

oEstPagina = oDoc.StyleFamilies.getByName("PageStyles")
oPagPadrao = oEstPagina("Default")
oPagPadrao.HeaderIsOn = True
oTxtCabecalho = oPagPadrao.HeaderText
oCursorCabecalho = oTxtCabecalho.createTextCursor()
oTxtCabecalho.insertTextContent(oCursorCabecalho, oGraph, False)
'oText.insertTextContent( oCursor, oGraph, False )



cFile = Left( cFile, Len( cFile ) - 4 ) + ".pdf"
cURL = ConvertToURL( cFile )

' Save the document using a filter.
oDoc.storeToURL( cURL, Array(MakePropertyValue( "FilterName", "writer_pdf_Export" ),)

oDoc.close( True )
End Sub


Function MakePropertyValue( Optional cName As String, Optional uValue ) As com.sun.star.beans.PropertyValue
Dim oPropertyValue As New com.sun.star.beans.PropertyValue
If Not IsMissing( cName ) Then
oPropertyValue.Name = cName
EndIf
If Not IsMissing( uValue ) Then
oPropertyValue.Value = uValue
EndIf
MakePropertyValue() = oPropertyValue
End Function

Por fim, o script final ficou assim:


#!/bin/bash

# Código utilizado para consulta através do JS
# "área^diretório onde está o artigo/nome do arquivo sem extensão*título do arquivo|palavras chaves separadas por vírgula (final),$autor1@autor2@~"

rm sucesso.txt > /dev/null 2>&1
rm fracasso.txt > /dev/null 2>&1

COD_PROCESSADO=0
AREA_PROCESSADO=""
FCOUNT=0
AREA=""
NOVO="y"

FILE="./list_trabalhos_com_oral.csv"

toUpper() {
echo -n $1 | python -c 'import sys, locale, codecs; sys.stdout = codecs.getwriter(locale.getdefaultlocale()[1])(sys.stdout, "replace"); print( sys.stdin.read().decode("utf8").upper() )'
}

# Coloca a descrição da forma de apresentação em minúsculo, tira os acentos, remove o início do texto "Sessão de " e substitui os espaços por _ e remove qualquer caracter de controle
processFrmApr() {
echo -n $1 | python -c 'import sys, locale, codecs; sys.stdout = codecs.getwriter(locale.getdefaultlocale()[1])(sys.stdout, "replace"); print( sys.stdin.read().decode("utf8").lower() )' | unaccent utf-8 | sed 's,sessao de ,,' | sed 's, ,_,g' | sed 's,[[:cntrl:]],,g'
}

# Coloca a descrição da área de conhecimento em minúsculo, tira os acentos e substitui os espaços por _ e remove qualquer caracter de controle
processArCnh() {
echo -n $1 | python -c 'import sys, locale, codecs; sys.stdout = codecs.getwriter(locale.getdefaultlocale()[1])(sys.stdout, "replace"); print( sys.stdin.read().decode("utf8").lower() )' | unaccent utf-8 | sed 's, ,_,g' | sed 's,[[:cntrl:]],,g'
}

# Descrição dos campos em FIELDS
# FIELDS[1] = Código do Banco de Dados
# FIELDS[2] = Título do Trabalho
# FIELDS[3] = Nome do Arquivo
# FIELDS[4] = Palavras Chaves separadas por ;
# FIELDS[5] = Autor
# FIELDS[6] = Área de conhecimento
# FIELDS[7] = Tipo do Trabalho

processLine () {

  for i in `seq 1 7`;
  do
      FIELDS[$i]=$(toUpper `echo $line | awk -F'\t' '{print $'$i'}'`)
  done

  # Gera o PDF dos arquivos submetidos e o arquivo para ser utilizado na busca pelo javascript
  # Se mudou a área que está sendo processada deve-se criar o arquivo que contêm os dados da área
  echo ${FIELDS[6]}
  if [ "x${FIELDS[6]}" != "x$AREA_PROCESSADO" ]; then
      AREA_PROCESSADO=${FIELDS[6]}
      # Termina o arquivo antigo
      echo -n "~" >> $AREA.txt
      AREA=$(processArCnh ${FIELDS[6]})
      rm $AREA.txt > /dev/null 2>&1
      touch $AREA.txt
      NOVO="y"
  fi
  echo $AREA_antiga

  # Se mudar o código é um novo trabalho, então faz a conversão para PDF e insere o cabeçalho do evento
  # Se mudou o código do trabalho, finaliza esta entrada no texto utilizado pelo javascript e inicia a nova
  if [ ${FIELDS[1]} != $COD_PROCESSADO ]; then
      if [ $COD_PROCESSADO != 0 -a "x$NOVO" != "xy" ]; then
          # Termina entrada apenas se não é a primeira mudança, pois o conteúdo anterior era vazio.
          echo -n "~" >> $AREA.txt
      fi
      COD_PROCESSADO=${FIELDS[1]}
      NOVO="x"

      echo "Convertendo arquivo: "${FIELDS[3]}

      # Cria pasta de comunicação oral ou painél se não existir
      FIELDS[7]=$(processFrmApr ${FIELDS[7]})
      echo -n ${FIELDS[7]}
      if [ ! -d ${FIELDS[7]} ]; then
          mkdir ${FIELDS[7]}
      fi

      # Cria a pasta da área de conhecimento do trabalho se não existir
      FIELDS[6]=$(processArCnh ${FIELDS[6]})
      if [ ! -d ${FIELDS[7]}/${FIELDS[6]} ]; then
          mkdir ${FIELDS[7]}/${FIELDS[6]}
      fi

      # Converte o arquivo para pdf e inclui o cabeçalho do SEU
      contador=1
      FCOUNT=`expr $FCOUNT + 1`
      while [ $contador -le 5 ]; do
          if [ -f upload2009/${FIELDS[3]}.pdf ]; then
              echo "Não é necessário converter"
              echo "Copiando" ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
              cp ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
              break
          fi

          if [ -f upload2009/${FIELDS[3]}.rtf ]; then
              echo "Iniciando conversão"
              /usr/lib/ooo-2.4/program/soffice.bin -invisible -headless "macro:///rtf2pdf.Conversion.ConvertWordToPDF(`pwd`/upload2009/${FIELDS[3]}.rtf)"
              # Arquivo convertido com sucesso
              if [ $? == 0 ]; then
                  echo "Copiando" ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
                  cp ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
                  break
              fi
          else
              echo "Não é necessário converter"
              if [ -f upload2009/${FIELDS[3]}.pdf ]; then
                  echo "Copiando" ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
                  cp ./upload2009/${FIELDS[3]}.pdf ./${FIELDS[7]}/${FIELDS[6]}/$FCOUNT.pdf
                  break
              fi
          fi
          contador=`expr $contador + 1`
      done

      if [ $contador -le 5 ]; then
          echo ${FIELDS[2]} convertido com sucesso >> sucesso.txt
      else
          echo ${FIELDS[2]} não convertido >> fracasso.txt
      fi
   
      # Insere no arquivo utilizado pelo javascript os dados do trabalho
      # Insere a área, o diretório onde está o artigo, o nome do artigo sem extensão e o título do artigo
      echo -n $AREA_PROCESSADO"^"${FIELDS[7]}"/"${FIELDS[6]}"/"$FCOUNT"*"${FIELDS[2]}"|" >> $AREA.txt
   
      # Insere as palavras chaves
      for i in `seq 1 3`;
      do
          PC[$i]=$(toUpper `echo ${FIELDS[4]} | awk -F';' '{print $'$i'}'`)
          echo -n ${PC[$i]}"," >> $AREA.txt
      done
      echo -n "$" >> $AREA.txt
   
      # Insere o primeiro autor
      echo -n ${FIELDS[5]}"@" >> $AREA.txt
  # Insere os demais autores
  else
      echo -n ${FIELDS[5]}"@" >> $AREA.txt
  fi
}

BAKIFS=$IFS
IFS=$(echo -en "\n\b")
exec 3<&0
exec 0<$FILE
while read line
do
  # use $line variable to process line in processLine() function
  processLine $line
done
exec 0<&3

# restore $IFS which was used to determine what the field separators are
IFS=$BAKIFS
exit 0


Muito legal esse monstrengo!!!

terça-feira, 30 de junho de 2009

Extrair o áudio de um vídeo no formato MP3

A linha de comando é mesmo um canivete suíço.

mencoder file.wmv -of rawaudio -oac mp3lame -ovc copy -o file.mp3


É como Master Foo disse uma vez: "There is more Unix-nature in one line of shell script than there is in ten thousands lines of C".

O google também é ótimo... achei essa linha em: http://knowledge.drun.net/viewtopic.php?id=174

domingo, 28 de junho de 2009

TORCS: O Simulador de Corrida Aberto

Estou fazendo um curso de pós-graduação na Unioeste sobre Desenvolvimento de Software e Novas Tecnologias. Até agora está um pouco chato, pois tá chovendo bastante no molhado, mas também já aprendi algumas coisas legais. Além disso, temos que desenvolver um artigo para o final do curso, sendo assim eu consegui juntar o útil com o agradável, ou seja trabalhar com desenvolvimento de jogos.

A versão final do artigo só deverá ser entregue em setembro de 2010, porém já tivemos que fazer um esboço do trabalho final. O título do trabalho que tenho até agora é: "Inteligência Artificial Aplicada a Evolução de Carros de Corrida sem Treinamento". Ainda não estou satisfeito com este título, pois acho que não sintetiza de forma clara o que quero fazer, mas como era só um esboço inicial ficou assim mesmo.

Neste projeto eu irei utilizar o jogo torcs, http://torcs.sf.net, e meu objetivo é utilizar técnicas de inteligência artificial para conseguir evolução de tempos de volta e que isto seja feito em tempo real, ou seja, durante a corrida. A idéia é fazer com que o "robô" seja sensível as mundanças da pista e tenha a sensibilidade para melhorar sua tocada, sendo assim ser capaz de chegar aos limites do carro.

O pessoal que desenvolve o torcs criou um tutorial muito legal sobre criação de "robôs": http://www.berniw.org/, através dele é possível desenvolver uma IA bastante completa, que permite um carro frear, acelerar (claro :-), passar marcha, voltar para a pista depois de uma rodada, realizar ultrapassagens e parar nos boxes.

Com todas estas características vou deixar um pouco de lado minhas brincadeiras com a CrystalSpace3D e vou me dedicar a terminar de implementar o que está descrito no tutorial, pois além de oferecer a implementação, existem muitas explicações e referências a documentações mais completas sobre os conceitos abordados. Uma ótima pedida a quem quer brincar com IA em jogos de corrida.

Com isso espero começar a contribuir também com o jogo, aproveitando o que já está implementado para oferecer um jogo completo de F1. Na verdade, não falta muito para se ter isso, o que já está implementado é bem legal para ser oferecido como um jogo, basta oferecer mais conteúdo relacionado a F1, tais como equipes e pistas. É claro que para fazer comparações com jogos comerciais ainda é necessário evoluir bastante, mas com toda a sinceridade, é o jogo de corrida mais legal que vi até agora para o mundo do software livre.

terça-feira, 9 de junho de 2009

Compilando CrystalSpace3D (CS) e Crystal Entity Layer (CEL) no Slackware 12.2

Estou brincando com a CS e a CEL por aqui. Se você pretende utilizar esta biblioteca para desenvolver jogos ou qualquer outra aplicação com recursos 3D eu diria para levar em consideração esta mensagem http://sourceforge.net/mailarchive/forum.php?thread_name=742a36470902230603y27874edaid465aec69e0f78fc%40mail.gmail.com&forum_name=crystal-main. No entanto, você é dono do seu sistema, então trabalhe com qualquer versão da biblioteca que você quiser.

Compilar tais bibliotecas é muito fácil, pois a docuemtação é simples, porém espera-se que você já tenha compilado algo, mas não deve fugir de um ./configure, make e make install e em alguns casos jam e jam install. Esta última é a ferramenta utilizada pela CS e CEL para compilar a árvore dos fontes.

A maioria das dependências utilizadas podem ser encontradas em www.slacky.eu e www.slackbuilds.org. Outra ferramenta interessante é a pkgcreate (http://www.din.uem.br/~msambinelli/download/), que facilita bastante o processo de criar pacotes a partir dos fontes.

As informações para compilar e instalar a CS está disponível em: http://www.crystalspace3d.org/docs/online/1.4/manual/Building.html#0

Algumas dependências já vêm instaladas com o Slack, são elas: zlib, libpng, libjpeg, freetype, bibliotecas de desenvolvimento do X e da OpenGL, libogg, libvorbis, libmng. Outras já possuem seus pacotes prontinhos para serem instaladas, são elas:

Jam: http://www.slacky.eu/aadm/pkgs/index.php?ver=6&pkg=658
wxWidgets 2.8.10: http://www.slacky.eu/aadm/pkgs/index.php?ver=6&pkg=1165
libmspack: http://www.slacky.eu/aadm/pkgs/index.php?ver=6&pkg=792 (dependência da wxWidgets acima)
CEGUI 0.6.2: http://www.slacky.eu/aadm/pkgs/index.php?ver=6&pkg=635

Outros possuem SlackBuild's prontos:

ODE 0.8: http://slackbuilds.org/repository/12.1/libraries/ode/ (utilizei a versão para o Slack 12.1 e que funciona muito bem no 12.2, pois esta é a versão recomendada para a CS). Aqui está o link para o pacote que compilei: ode-0.8-i486-1_SBo.tgz

Não instalei algumas dependências, pois não pretendo utilizar, sendo ela a biblioteca Bullet, pois pretendo utilizar a ODE.

Estas bibliotecas compilei utilizando o pkgcreate:

cal3d-0.11.0-i686-1_PKGC.tgz
lib3ds-1.3.0-i686-1_PKGC.tgz
libcaca-0.99.beta13-i686-1_PKGC.tgz (o pacote oficial do Slack deve ser substituído por este, pois esta versão é requerida, porém tal biblioteca é opcional, sendo apenas necessária se você quiser a saída do seu programa em ASCII :-)

Binários da Cg Toolkit podem ser encontrados no site http://developer.nvidia.com/object/cg_toolkit.html. Aqui instalei a versão 2.1, porém a partir do site da CS, qualquer versão acima da 1.4 deve funcionar.

Muito bem, depois de instalado todos esses arquivos, basta compilar a CS. Baixe os fontes através do comando:

svn co https://crystal.svn.sourceforge.net/svnroot/crystal/CS/branches/release/V1.4 CS_1.4

Agora vá tomar um café enquanto os fontes são baixados. Depois disso, acesse o diretório criado e agora digite ./configure e make ou jam (neste caso o make é uma capa para o jam). E vá tomar outro café :-). Após alguns minutos ou horas, dependendo da sua máquina você estará com a CS compiladinha :D. Não irei dizer para você instalar a biblioteca, pois tive problemas ao fazer isso, o sistema de detecção de colisões não funcionou corretamente, então utilize a biblioteca a partir do local onde esta está.

Vamos partir para a CEL agora, que é uma biblioteca de entidades construída sobre a CS que pode ser considerada uma engine para jogos. Assim como a CS, as informações para compilar e instalar a CEL também estão disponíveis online: http://crystalspace3d.org/cel/docs/online/manual-1.2/Building.html#0. Tais informações não são muito completas, mas já é melhor do que nada.

Esta é bem mais fácil para compilar, já que não exige a instalação de muitas dependências, pois python é padrão em qualquer distribuição. Além desta existe um pacote opcional, o HawkNL, caso você queira suporte a redes, o que eu acho uma ótima idéia. Para esta dependência eu não encontrei pacotes e também não consegui gerar o pacote através do pkgcreate e também fui preguiçoso o suficiente para apenas compilá-la e instalá-la, sem criar pacote algum. O site da HawkNL é http://www.hawksoft.com/hawknl/. Outra coisa importante é exportar a variável CRYSTAL com o caminho onde você compilou a CS.

Pronto, feito isso basta rodar o comando "jam" e ir tomar um café. Após isso você já pode utilizar a biblioteca para criar seus próprios programas assim como rodar os vários exemplos que vem com ela. Para rodar os exemplos você deve exportar a variável LD_LIBRARY_PATH para apontar para onde você compilou a CS.

terça-feira, 26 de maio de 2009

Estatísticas na web é com o motigo

Há algum tempo vinha tentando encontrar um analisador de estatísticas legal. Tinha um que usei a muito tempo atrás mas que não conseguia me lembrar o nome.

Fiz uma procura no Google pela minha antiga página, porém só conseguir encontrar alguns links quebrados para ela, pois esta já não existe mais :P

Procurei pelo arquivo da web e depois de algums minutos o encontrei: www.archive.org lá estava alguns resquícios da minha antiga página sobre simuladores de corrida: http://web.archive.org/web/20021206214238/http://www.speedracing.com.br/gpxvirtual

E no rodapé da página estava as estatísticas que eu tanto queria http://webstats.motigo.com/ que para minha surpresa ainda mantem o serviço no ar e com as mesmas funcionalidades gratuitas, mas ainda sem tradução para o português. Por fim, não sei se é saudosismo, mas eu gostava muito mais da interface antiga do serviço!

Slackware com GSB e acentos em pendrives e compartilhamentos via samba no nautilus

O Slackware não suporta oficialmente o GNOME, porém diversos projetos o empacotam. Depois de utilizar por um bom tempo o Dropline GNOME hoje utilizo o GSB por uma questão de filosofia, ele é menos intrusivo ao sistema.

Uma coisa que estava me chateando era toda vez que eu inseria um pendrive os acentos apareciam com aquele losângulo preto e um ponto de interrogação e ainda com o texto (Codificação Inváliada) no final do nome do arquivo. Com certeza deveria ter alguma solução para isso, e num belo dia resolvi procurar... bingo, basta acrescentar a string "utf8" na chave "/system/storage/default_options/vfat/mount_options". Com isto os acentos devem aparecer corretamente, visto que a maioria possui sistema de arquivos FAT32 e têm seu arquivos gravados através do WindowsXP que usa esta codificação. Talvez exista alguma mágica para detectar a codificação dos arquivos, mas por enquanto estou bastante satisfeito com esta solução.

Outro problema que tive foi em relação a acessar um compartilhar através do Nautilus com o samba, através do protocolo SMB. Depois que solucionei o problema achei bem legal como o samba trata com codificações, ele possui uma codificação para o servidor e outra para o cliente e usa esta informação para converter os nomes de arquivos de uma máquina para outra. No meu caso, a minha máquina servidora um WindowsXP utiliza a codificação utf8, já no meu netbook com Slack, utilizo iso8895-1 e quando acessava o compartilhamento o samba por default tem a configuração da codificação do cliente em utf8, então trazia o nome do arquivo neste formato, que na hora de ser exibido em iso8895-1 dava problema. Para resolver o problema basta editar o arquivo /etc/samba/smb.conf e adicionar a seguinte linha:

unix charset = iso8895-1

Pronto, agora os caracteres com acento aparecem perfeitamente... bem, se tiver algum nome de arquivo em Japonês no meu compartilhamento eu vou ter problemas, mas isso não é algo que vou me preocupar agora :)

segunda-feira, 9 de março de 2009

MSI Wind U100X

Ganhei este lindo brinquedinho da minha mãe! :D

Desde que vi os netbooks me apaixonei, finalmente via um notebook que era realmente móvel: pequeno e leve! Algo que eu não via nos notebooks por aí.

Porém, confesso que fiquei bastante decepcionado quando vi os primeiros netbooks. Pouco espaço em disco, pouca memória e uma tela pequena demais. Eu esperava algo melhor e um espaço confortável para acessar internet, programar, etc, etc.

Foi então que a coisa começou a evoluir e por dias fiquei babando por estas maquininhas. Quando vi o MSI Wind U100X achei a máquina completa, porém havia me programado para comprar um daqui 1 ano e meio, foi então que recebi uma grade presente e agora estou aqui, com meu Wind de 10'', 2GB de RAM, 120 GB de HD, Intel Atom 1.6Ghz, bateria 6 células, Wireless e webcam 1.3 Mpixel de onde faço este post!

Estou extremamente satisfeito com meu novo brinquedinho e já instalei minha distribuição GNU/Linux preferida nela, a Slackware. Agora que tenho uma máquina minha, sim programadores precisam de uma máquina só para eles, vou continuar minhas aventuras com a CrystalSpace! Falando nisso, estou até compilando ela agora, mas isso é assunto pra outro post...

quinta-feira, 12 de fevereiro de 2009

Incrementado variável, com valor inicial, de 1 em 1 a cada página no iReport 3.0.0

Esses dias um amigo e colega de trabalho, o Anselmo do blog que acompanho, resolveu algum problema cabeludo. Não perguntei o que era, só lembro dele dizendo: "Essa merece um post" e eu dei risada... Agora me encontro na mesma situação.

Acreditei que seria fácil fazer uma variável incrementar no iReport a partir de um valor inicial a cada página, porém após praticamente três horas de luta saí da frente do PC exausto, mas com a luta ganha!

Após diversas tentativas e buscas de como fazer essa tarefa, acreditei ser impossível conseguir o efeito que desejava, no entanto, logo chegou um insight que me deu novas esperanças. Pensei em utilizar mais uma varíavel. Deu certo, porém o valor na primeira página ficava em "null". Quando estava quase pensando em jogar a toalha veio outro insight e pensei "vou jogar a declaração desta variável auxiliar para cima da que é utilizada na página"... e para minha surpresa, deu certo!!!!

Depois da historinha, agora a parte interessante, como ficou as minhas variáveis:

Variável Gambi: esta varíavel inicia em 0 (zero) e a cada página tem seu valor incrementado em 1 (um).
Tipo de Classe da Variável: java.lang.Integer
Tipo de Cálculo: Contador
Resetar Tipo: Relatório
Tipo de Incremento: Nenhum
Variável de Expressão: $V{Gambi}
Valor Inicial da Expressão: new Integer(0)

Variável NumMemorando: esta é a varíavel que vai aparecer no relatório e que a cada página deve ser incrementada.
Tipo de Classe de Variável: java.lang.Integer
Tipo de Cálculo: Sistema
Resetar Tipo: Nenhum
Tipo de Incremento: Página
Variável de Expressão: new Integer ($P{NmrMmrInicial}.intValue() + $V{Gambi}.intValue())

Pronto, agora é só utilizar seu contado a partir de um valor inicial!

Mais uma vez, na lista de variáveis do iReport, a variável Gambi deve estar listada primeiro que a variável NumMemorando, senão você terá um valor "null" na primeira página.

sábado, 3 de janeiro de 2009

Brincando com Jogos de Corrida

Desde muito pequeno eu sempre gostei de jogos de corrida, até mesmo porque eu sempre vencia meus irmãos :D.

O primeiro jogo/simulador de fórmula 1 que joguei foi o F1GP ou World Circuit. Tem uma página legal na wikipédia sobre ele: http://en.wikipedia.org/wiki/Formula_One_Grand_Prix_(Geoff_Crammond) e no YouTube tem um vídeo legal também: http://www.youtube.com/watch?v=ZPTN4hnuShw. Neste vídeo contém a abertura do jogo que até hoje me deixa emocionado :). Eu e meus irmãos passavamos horas jogando. Foi paixão a primeira jogada, ainda lembro até hoje a minha primeira vitória, em Mexico City, estava numa posição ruim, 13º, e fiz uma corrida perfeita, voei baixo, fiz melhor volta atrás de melhor volta e no final lembro que o meu pai imprimiu a classificação final... fahsdufashfasdufsdhfsuadfshdu, bons tempos!!!

Depois vieram os outros jogos da série, GP2, GP3 e GP4. Joguei todos e joguei muito! Até hoje arranjo um tempinho pra jogar GP4, apesar de ser um jogo de 2002, ainda consigo me divertir com ele, ainda mais no meu hardware modesto, que não rodam os jogos de F1 mais novos e mesmo se rodasse continuaria jogando GP4, pois já tive a oportunidade de jogar outros, mas sempre tem algum aspecto que me faz voltar, seja ele na IA, na física, na jogabilidade com o teclado, etc.

Hoje já estou com quase 25 anos, acabei de me formar em ciência da computação e mesmo antes de entrar na faculdade eu sempre quis desenvolver um jogo, porém por um motivo ou por outro sempre acabava mudando os planos e nunca passava da votande. Nas últimas semanas decidi mudar esta história e criar culhões :D. Finalmente comecei a estudar pra criar um jogo e até agora estou me divertindo bastante, apesar de quebrar a cabeça muitas vezes pra fazer algo funcionar. Ainda não comecei a estudar a parte de física a fundo pra deixar o jogo realístico, pois primeiro quero criar um jogo que se possa jogar :D. Isso parece contraditório, porém sou usuário/programador de Linux e neste ambiente é comum encontrar diversos jogos que não dá pra instalar e jogar. Tem que instalar, ficar procurando mods, pistas, add-ons em diversos lugares, aprender como instalá-los, etc, etc, etc... fora os jogos incompletos que possuem screenshots marivolhasas em seus sites, parece até que os desenvolvedores ficaram mais tempo programando pra conseguir uma screen legal, ao invés de um jogo legal e isso me deixa muito frustrado.

Sendo assim a minha idéia é começar com coisas simples, porém juntando todas elas seja possível se divertir ao menos de forma parecida como eu me diverti com o F1GP. Você entra no jogo, escolhe as opções e joga, e pode ficar jogando durante horas. Não me importo em fazer algo visualmente atrativo, mas algo atrativo como um tudo, se conseguir isso, tenho certeza que poderei agregar uma comunidade com interesse comuns. Ainda tenho ótimas lembranças das comunidades que participei durante todos os anos que fiquei jogando a série GP, a qual não apenas jogava, mas criava utilitários, patchs, pistas, carros, milhares de add-ons pra deixar o jogo mais legal. Mas por enquanto isso é apenas sonho, ainda tem muito trabalho pra que eu consiga qualquer coisa próxima disso, então vamos falar de uma coisa mais interessante, o que eu estou fazendo (pelo menos pra min é interessante :).

Primeiramente, decidi em utilizar um engine para jogos, pois caso contrário iria levar no mínimo 2 anos pra fazer uma que funcionasse, e ainda não seria muito boa, pra somente depois começar a desenvolver o jogo. Deste modo, comecei a pesquisar algumas engines em software livre/código aberto. Decidi que iria utilizar a Delta3D, mas depois de praticamente uma semana tentando compilá-la desisti. Depois disto decidi pela CrystalSpace3D (CS) a qual foi fácil fazer funcionar, além disso esta possui uma boa documentação introdutória, a qual está desatualizada, mas quebra um galho pra quem está inciando.

Tal engine possui diversos exemplos que podem ser estudados, modificados e executados imediatamente, além disso, possui capacidade de scripting através da linguagem python e de uma linguagem própria, além disso possui uma biblioteca construída sobre si denominada CEL, esta contém abstrações para facilitar a criação de entidades. Algo que achei bastante interessante quando li e que gostei mais ainda da idéia depois que comecei a ler Programming Game AI by Example.

Apesar de aparentar ser uma boa engine, com suporte a física, detecção de colisões, rede e som (os elementos que preciso pra desenvolver um jogo) possui alguns bugs (que estão sendo trabalhados) e como já disse, como a documentação é introdutória, muitas coisas é difícil de saber como deve ser utilizada, porém a comunidade de desenolvedores é bastante ativa e disposta em ajudar, até já obtive a recomendação de um jogo desenvolvido com a engine que está hospedado no site SourceForge.net, falando nisso, existem vários jogos desenvolvidos com a engine lá, o que é legal, pois pra quem está começando fica mais fácil aprender com pessoas que já têm experiência.

Depois disso, decidi criar uma pista. Não fazia a menor idéia de como fazé-lo, sendo assim a minha primeira idéia foi encontrar uma pista já pronta e encontrei, porém não satisfez as minhas necessidades, pois estava em um formato não suportado pela engine e que só abriria no 3D Studio Max. Deste modo optei por fazer algo utilizando o Blender e fui incentivado pelo pessoal da CS, pois este é o melhor programa suportado pela engine.

Como nunca tinha utilizado o Blender pesquisei na internet como desenvolver uma pista com ele e pra minha surpresa encontrei um ótimo tutorial a respeito. O resultado final que obtive não foi nada profissional, mas para quem não sabia nada de Blender (ainda me bato bastante pra fazer umas coisas simples) fiquei bastante satisfeito, pois pude ver minha pista carregada dentro da engine e até interagir com ela.

Nesta screenshot está a pista dentro do blender:



E esta é uma screenshot da mesma pista dentro da engine:



Como já havia dito, nada profissional, mas consegui criar estas coisas de forma relativamente fácil. Agora que já tenho uma pista (que precisa de alguns ajustes, na verdade são diversos, mas vou fechar o parêntese :) o meu próximo passo é colocar um carro pra andar sozinho aí. Acreditava que isso seria extremamente difícil, mas pelo que estou lendo até agora em Programming Game AI by Example esta não parece uma tarefa muito herculiana e acho que vou me divertir fazendo isso.

Assim que colocar um carro pra rodar aí faço uma nova postagem a respeito. Por hoje é só :D