h1

Módulos de Região – Finalmente

dezembro 11, 2009

Atrasei mais uma vez esse post, mas agora é para valer, vamos fazer um passo a passo de como fazer um módulo de região para o OpenSim! Estive batalhando com módulos de região nos últimos dias e posso dizer que estou afinadíssimo para as instruções. Vou assumir algumas ferramentas (no caso Windows, .NET e Visual Studio C#), mas também desenvolvo e Linux e não é muito diferente (Linux, Mono, Monodevelop).

Vamos lá então, mão a obra!

1. OpenSim

Para começarmos a desenvolver um módulo de região, precisamos do próprio OpenSim, naturalmente. Siga as instruções da wiki, baixe o OpenSim, compile e teste. Não vou entrar nos detalhes de configuração para não fugir do foco. O importante é que todas as bibliotecas do OpenSim estejam compiladas, vamos referencia-las no próximo passo.

2. Começando um Novo Projeto

Abra o Visual Studio C# e comece uma nova solução (no meu caso chamei de OpenSimModule) e um novo projeto (usei o mesmo nome). No canto esquerdo você verá uma coluna com o nome da solução, do projeto, e dentro, uma pasta de references. Vamos precisar referenciar algumas bibliotecas. Clique o botão direito em References e escolha Add Reference. Ao abrir a janela, vá na aba Browse, siga até o local onde está sua instalação do OpenSim, entre na pasta bin, e escolha os seguintes dlls:

log4net, Mono.Addins, Nini, OpenMetaverse, OpenMetaverseTypes, OpenMetaverse.StructuredData, OpenSim.Framework, OpenSim.Region.Framework.

Adicione também as dlls de sistema System, System.Data e System.XML.

Lembrando que essas dlls são as que o seu projeto poderá referenciar com o “using”, portanto quais dlls você precisa depende de o que você quer fazer. O OpenSim.Region.Framework e OpenSim.Framework sempre serão necessários, pois eles contém os objetos da região e do OpenSim respectivamente. OpenMetaverse são as bibliotecas que o OpenSim usa para implementar o servidor do SL e também são úteis, Nini é para carregar configurações do arquivo, log4net é para usar a biblioteca Logger para imprimir dados no console e finalmente, Mono.Addins serve para encaixar o módulo no OpenSim. Cobriremos os usos no código mais adiante.

Após adicionada as referências, crie um novo item no projeto, uma classe de C# vazia, e chame do que quiser (no meu caso, chamei de MyModule).

3. Programando o Módulo

3.1 Início

Está tudo pronto para começarmos nosso módulo de região! Para começar o código, vamos incluir todas as bibliotecas que precisamos e vamos setar o Mono.Addins para que o OpenSim reconheça esta biblioteca como um módulo de região. Comece a classe desta forma:

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using OpenSim.Framework;
using OpenSim.Region.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Interfaces;
using Mono.Addins;

using OpenMetaverse;
using Nini.Config;
using log4net;

[assembly: Addin("OpenSimModule", "0.1")]
[assembly: AddinDependency("OpenSim", "0.5")]
namespace OpenSimModule
{
    [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
    public class MyModule : ISharedRegionModule
    {
        private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        List<Scene> m_scenes = new List<Scene>();
        Dictionary<Scene, List<SceneObjectGroup>> scene_prims = new Dictionary<Scene, List<SceneObjectGroup>>();
        int counter = 0;
        bool positive = true;

As únicas modificações que você deve fazer é o nome da classe e a primeira linha do assembly (que também será o nome do seu módulo). As outras linhas com “[” são para o Mono.Addin reconhecer que se trata de um módulo de região. A classe extende o método ISharedRegionModule, vamos falar um pouco sobre isso.
Existem dois tipos de módulos de região, um é o ISharedRegionModule e o outro é o INonSharedRegionModule. A diferença é simples: o ISharedRegionModule instancia o módulo uma única vez, e cada vez que uma nova região é adicionada ao grid, a instância é notificada. No INonSharedRegionModule, cada vez que uma região entra no grid, uma nova instância do seu módulo é criado só para atender aquela região. As diferenças ficaram claras quando entrarmos na descrição dos métodos do IRegionModuleBase.

3.2. Métodos da Interface dos Módulos:

Descrição e exemplo dos métodos que devem ser implementados para se fazer um módulo de região.

3.2.1. Initialise:

O primeiro método de um módulo de região é o Initialise. Ele é o primeiro método executado, quando o simulador ainda está carregando. Nessa parte do código a região (Scene) ainda não existe. Essa parte é geralmente utilizada para ler as configurações de arquivo usando o parâmetro passado (IConfigSource config). É possível ler qualquer opção que esteja no OpenSim.ini (veja esse manual para saber como). Como não precisamos de nenhuma configuração neste exemplo, só iremos imprimir uma mensagem que o módulo foi inicializado:

        public void Initialise(IConfigSource config)
        {
            m_log.Info("[HELLOWORLD] Initializing...");
        }

3.2.2. Post Initialise:

Só existe em ISharedRegionModule, é chamado apenas uma vez, após todas as regiões terem sido instanciadas. Como o INonSharedRegionModule só tem uma região, não havia necessidade de um post initialise. Não temos necessidade para esse método no nosso exemplo.

        public void PostInitialise()
        {
        }

3.2.3. AddRegion:

É o método chamado cada vez que uma nova região é adicionada ao grid. Pode ser chamada várias vezes em um módulo compartilhado e apenas e um módulo não compartilhado. Aqui é o lugar para inicializar qualquer coisa que dependa da cena. No nosso caso, estamos guardando a referência da cena no nosso vetor de cenas, para podemos usar no futuro. O método recebe como parâmetro a cena que está sendo adicionada.

        public void AddRegion(Scene scene)
        {
            m_scenes.Add(scene);
        }

3.2.4. RegionLoaded

É chamado depois que todos os métodos a serem adicionados já foram adicionados. Neste ponto, todos os outros módulos já passaram por AddRegion, portanto é o momento para criar hooks com outros módulos e se inscrever nos seus eventos. No nosso caso, aproveitamos o momento para nos inscrevermos no evento OnFrameDelegate, que conforme já explicado em outro post, é o evento da batida de coração do OpenSim. Não é muito recomendado se inscrever neste método com muitos módulos, pois esse método já tem muito o que fazer sem ter módulos extras puxando ele pra baixo, mas para efeito de exemplo, estamos nos prendendo a ele.

        public void RegionLoaded(Scene scene)
        {
            scene.EventManager.OnFrame += new EventManager.OnFrameDelegate(OnTick);
            DoHelloWorld(scene);
        }

Mais para frente, veremos os detalhes do método OnTick e do método DoHelloWorld.

3.2.5. RemoveRegion

A mesma idéia do AddRegion, mas nesse caso é chamado quando a região é removida do grid. Aqui deve-se fazer as limpezas necessárias para garantir que os módulos continuem funcionando, ciente que esta região já não existe mais. No nosso exemplo, no desinscrevemos do evento e retiramos a referência da lista de cenas.

        public void RemoveRegion(Scene scene)
        {
            scene.EventManager.OnFrame -= new EventManager.OnFrameDelegate(OnTick);
            m_scenes.Remove(scene);

        }

3.2.6. Close

É o método chamado quando o módulo está fechando. Usado para fazer limpezas permanentes, como apagar assets, remover referências, etc. Após este método, o módulo é fechado. No caso, não temos nada do gênero para fazer.

        public void Close()
        {
        }

UPDATE:

Como foi apontado pelo Ricardo nos comentarios, ha novos metodos que precisam ser implementados do tempo que eu escrevi este tutorial. Sao eles:

3.2.7. Name:

Retorna o nome do modulo.

         public string Name{
             get{ return “MyHello”; }
         }

3.2.8. ReplaceableInterface:

          public Type ReplaceableInterface{
              get{ return null; }
           }

3.3. Métodos Extras do Módulo:

Após ver os métodos necessários para um módulo, colocaremos aqui o nosso próprio método, que será o método que irá fazer o hello world se mover pela região. Não é necessário, mas é boa política fazer um método separado para tratar a funcionalidade do módulo, e não sobrecarregar os outros métodos padrões. No nosso exemplo, temos 2 métodos, o DoHelloWorld e o OnTick. O OnTick está ligado ao evento de frames e o DoHelloWorld é o responsável por criar os objetos que formarão a palavra Hello World.

        void DoHelloWorld(Scene scene)
        {
            // We're going to write HELLO with prims

            List<SceneObjectGroup> prims = new List<SceneObjectGroup>();
            // First prim: |

            Vector3 pos = new Vector3(120, 128, 30);
            SceneObjectGroup sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(0.3f, 0.3f, 2f);
            prims.Add(sog);

            // Second prim: -
            pos = new Vector3(120.5f, 128f, 30f);
            sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(1, 0.3f, 0.3f);
            prims.Add(sog);

            // Third prim: |
            pos = new Vector3(121, 128, 30);
            sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(0.3f, 0.3f, 2);
            prims.Add(sog);

            // Fourth prim: |
            pos = new Vector3(122, 128, 30);
            sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(0.3f, 0.3f, 2);
            prims.Add(sog);

            // Fifth prim: - (up)
            pos = new Vector3(122.5f, 128, 31);
            sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(1, 0.3f, 0.3f);
            prims.Add(sog);

            // Sixth prim: - (middle)
            pos = new Vector3(122.5f, 128, 30);
            sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(1, 0.3f, 0.3f);
            prims.Add(sog);

            // Seventh prim: - (low)
            pos = new Vector3(122.5f, 128, 29);
            sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(1, 0.3f, 0.3f);
            prims.Add(sog);

            // Eighth prim: |
            pos = new Vector3(124, 128, 30);
            sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(0.3f, 0.3f, 2);
            prims.Add(sog);

            // Ninth prim: _
            pos = new Vector3(124.5f, 128, 29);
            sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(1, 0.3f, 0.3f);
            prims.Add(sog);

            // Tenth prim: |
            pos = new Vector3(126, 128, 30);
            sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(0.3f, 0.3f, 2);
            prims.Add(sog);

            // Eleventh prim: _
            pos = new Vector3(126.5f, 128, 29);
            sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(1, 0.3f, 0.3f);
            prims.Add(sog);

            // Twelveth prim: O
            pos = new Vector3(129, 128, 30);
            sog = new SceneObjectGroup(UUID.Zero, pos, PrimitiveBaseShape.CreateBox());
            sog.RootPart.Scale = new Vector3(2, 0.3f, 2);
            prims.Add(sog);

            // Add these to the managed objects
            scene_prims.Add(scene, prims);

            // Now place them visibly on the scene
            foreach (SceneObjectGroup sogr in prims)
            {
                scene.AddNewSceneObject(sogr, false);
            }
        }

Ok, agora há muito o que esclarecer, então vamos com calma, e vamos descrever em partes o que está acontecendo neste trecho.

SceneObjectGroup e SceneObjectPart

Estes são os blocos de construção do OpenSim. São os objetos do mundo. Todo objeto, também chamado de primitive, é representado internamente por um SceneObjectPart, que tem todas as informações do objeto (onde está, seu tamanho, rotação, nome, dono etc). Um SceneObjectGroup é uma coleção de um ou mais SceneObjetParts (no caso de mais de um, são vários SceneObjectParts linkados). Para um objeto aparecer em uma cena, ele precisa ser um SceneObjectGroup. Todo SceneObjectGroup tem um RootPart, que é o SceneObjectPart principal do grupo.

Vector3

Simples, é um objeto que guarda a posição, X, Y e Z genérica para uso de coordenadas cartesianas. No caso, usamos para posição do objeto.

sog.RootPart.Scale

Método pertencente ao SceneObjectPart que muda as dimensões do objeto.

PrimitiveBaseShape.CreateBox()

Cria um primitive novo, no caso uma caixa (cubo).

Pronto! Agora conseguimos entender o que este trecho de código faz. Ele inicializa diversas posições, cria cubos nessas posições e mudam suas dimensões, para fazer as partes das letras, guardando as referências na lista prims. Depois de terminar todas as letras, guarda a referência da lista de prims em um dicionário cena, lista de prims, e adiciona todos esses prims a cena usando o método AddNewSceneObject. O segundo parâmetro do método determina se esse objeto novo adicionado na cena deve ser salvo no banco de dados ou não.

Vamos analisar agora o método OnTick:

        void OnTick()
        {
            if (counter++ % 50 == 0)
            {
                // Uncomment if you want to see this on your console
                m_log.Debug("[HELLOWORLD] Tick!");
                foreach (KeyValuePair<Scene, List<SceneObjectGroup>> kvp in scene_prims)
                {
                    foreach (SceneObjectGroup sog in kvp.Value)
                    {
                        if (positive)
                            sog.AbsolutePosition += new Vector3(5, 5, 0);
                        else
                            sog.AbsolutePosition += new Vector3(-5, -5, 0);
                        sog.ScheduleGroupForTerseUpdate();
                    }
                }
                positive = !positive;
            }
        }

O que este código faz é fazer a palavra hello world andar pela tela a cada frame do OpenSim. Primeiro, ele espera 50 ticks para agir. Se ele agisse em cada Tick, o processador ia disparar de tanto ter que atualizar o grupo, um tick é um espaço de tempo muito pequeno. O primeiro foreach garante que todas as cenas são iteradas, e o segundo foreach itera sob cada SceneObjectGroup, mudando a propriedade AbsolutePosition do grupo, que é um Vector3 de posição, com +5 e depois -5, em X e em Y. Desse modo, a cada frame, a palavra hello world irá se movimentar + ou – 5 (andando de um lado para o outro). Finalmente, ele faz um sog.ScheduleGroupForTerseUpdate(), que coloca esse SceneObjectGroup na fila de atualizações de posição que o simulador constantemente envia para os clientes. Dessa forma, é agilizado o processo de enviar as mudanças desse grupo para os clientes, normalmente isso é feito por um timer.

Fim do Tutorial

Demorou mas finalmente consegui escrever este tutorial! Espero que tenham gostado. Para um próximo post, prometo descrever mais coisas que podem ser feitas dentro de um módulo, para incentivar novas idéias, mas a princípio, fuçando nos objetos do OpenSim, você pode aprender o necessário para fazer o que a sua imaginação permitir.

h1

Dando Notícias…

novembro 13, 2009

Acabei quebrando novamente minha promessa de postar toda semana, última sexta foi uma loucura de problemas com uma região que estava quebrando direto. Estava com uma quantidade grande demais de prims ligados e o OpenSim simplesmente não estava aguentando.

Bom, só vir pedir desculpas aos pouquíssimos (mas muito apreciados) leitores do blog pela falha, e para compensar, eu finalmente vou postar o tutorial de como fazer uma módulo de região! (eeee, façam a hola)
Levei uma hora e meia só para eu entender como faz, mudou muito desde a última vez que aprendi. Esse tutorial é tão novo que ainda não está nas wikis do OpenSimulator, então aproveitem o privilégio! Não vou conseguir postar hoje, mas agora que já fiz funcionar, é só explicar. Então devo estar postando semana que vem!

Abraço a todos, e muita paciência!

h1

O Coração do OpenSimulator

outubro 27, 2009

Olá a todos, como sempre muito corrido, mas resolvi tirar alguns minutos do dia de hoje para adicionar um tópico rápido: o coração do OpenSim, ou mais precisamente, o coração de uma região.

Ao contrário do que se imagina ao se ver o mundo virtual criado pelo OpenSim, o tempo não é uma medida contínua como é no mundo em que vivemos (será que o mundo em que vivemos é mesmo um tempo contínuo? Dúvida filosófica para levar para casa). Nós temos a impressão de continuidade graças a arquitetura cliente-servidor. Neste caso, o servidor (OpenSim) é discreto e o cliente (Second Life Viewer, ou outros) é não discreto. Então você vê seu avatar andando continuamente, mas o servidor está processando as mensagens de modo discreto (em média, 10 vezes por segundos).

O que quero dizer é que a região não está processando um loop infinito constante, imagino que todos os desenvolvedores saibam o quanto isso é ineficiente. O que se faz então é criar uma batida de coração (heartbeat), e durante essa batida,todos os eventos serão tratados e todas as tarefas serão cumpridas, e então a região volta a dormir. Este heartbeat é um método chamado Update, e fica localizado em OpenSim.Region.Framework.Scenes.Scene , que é o objeto responsável por todo controle da região:

public override void Update()
        {
            int maintc = 0;
            while (!shuttingdown)
            {

No fim deste método, encontramos o seguinte código:

maintc = Environment.TickCount - maintc;
                maintc = (int)(m_timespan * 1000) - maintc;

                if ((maintc < (m_timespan * 1000)) && maintc > 0)
                    Thread.Sleep(maintc);
            }
        }

O que isso faz é simplesmente é garantir que se qualquer tarefa demorar mais do que um heartbeat, esta tarefa será jogada para o próximo, e a thread vai dormir (mais ou menos como largar a caneta as 18h como todos nós fazemos). A verdade é que o servidor não precisa muito mais do que isso, o OpenSim é mais parecido com uma agência de correios, o trabalho dele é só direcionar as mensagens dos clientes para os objetos certos e vice versa.

A parte interessante para vocês desenvolvedores de módulos, é que um módulo pode (e muitas vezes deve) monitorar estes heartbeats. Digamos que queira fazer um cubo passear na tela a uma certa velocidade, usando um módulo de região. Como você controlaria a velocidade? Uma idéia seria requisições de tempo para o servidor, no entanto, como o tempo do servidor é discreto, você pode se encontrar em situações que, por causa de 1 milésimo de segundo, você perde o heartbeat atual e só vai entrar no próximo. Repita a fórmula e logo seu cubo estará fora de sincronia com o tempo.

A solução é simples, basta se conectar ao evento de heartbeat. Deste modo seu módulo sempre estará ciente do tempo do servidor e ficará em sincronia com os outros eventos. Não vou entrar em muitos detalhes, pois ainda preciso elaborar o tutorial de módulos de região, mas para se anexar a um evento no módulo, basta fazer:”

scene.EventManager.OnFrame += new EventManager.OnFrameDelegate(OnTick);

Onde scene é o objeto Scene da região em questão e OnTick é o método que irá tratar o evento. OnFrame é o evento disparado a cada heartbeat. Mais detalhes quando fizer o tutorial de um módulo de região.

h1

Volta das Férias

outubro 18, 2009

Olá a todos que acompanham este novo blog, estou de volta das férias, que por sinal foram muito boas. Pretendo continuar as colunas do blog em breve, no entanto devido a complicações na minha vida pessoal, meu próximo post talvez venha um pouco atrasado.

Só para não deixar este tópico sem nenhuma relação com OpenSim, gostaria de trazer notícias das trincheiras do projeto. No período que estive fora, mais precisamente na semana em que voltei, houve diversas atualizações de performance por parte dos desenvolvedores, principalmente do nosso novo desenvolvedor do core, John Hurliman, e nosso já conhecido contribuinte da Intel, Dan lake, e conseguimos atingir a meta de 85 avatares em uma região!! Para os que não sabem, antes de eu me envolver com o projeto, o OpenSim suportava em torno de 15 usuários máximo em Linux. Começamos a derrubar fronteira por fronteira até atingirmos esse número impressionante.

Com esta quantidade de usuários, naturalmente a região não segurou por muito tempo, mas por outras razões de stress do código que provavelmente expôs alguns deadlocks e problemas de concorrência, já há um bom número de desenvolvedores atrás desses problemas.

Esperamos o apoio de toda a comunidade para chegarmos ao 1.0, esse resultado certamente nos trouxe quase lá.

Abraços e até meu próximo post, que pretendo que seja um pouco maior.

h1

Férias

setembro 4, 2009

Vou entrar de férias a partir de amanhã, 5 de setembro e voltarei só 7 de outubro, portanto sem posts até lá. Não terei tempo de fazer um posto completo hoje, então só queria deixar um vídeo interessante para os que mexem com Mundos Virtuais, uma versão misturada com Realidade Virtual, muito bom.

h1

Diagrama de Classes

agosto 28, 2009

Opensim-small
Finalmente um post pequeno.. 🙂
Só queria mostrar rapidamente, um diagrama de classes muito desatualizado (Agosto de 2007). Dá para ter uma idéia melhor da dimensão deste projeto. A imagem não é legível, é só para ter idéia de dimensão mesmo. O projeto hoje deve ser 4, 5 vezes maior..

h1

Módulos de Região

agosto 28, 2009

Olá novamente, peço desculpas pela demora nas atualizações do blog, ainda estou em acostumando com a idéia. Vou continuar mantendo a idéia de um post por week, embora provavelmente perderei algumas semanas nas férias que estou prestes a entrar, ou com imprevistos nas minhas sextas (semana passada tive que redigir uma proposta de patente). A razão de ser sexta feira o dia dos meus posts é que a IBM incentiva a idéia de um Think-Friday, dedicar sexta (ou um dia da semana) para projetos criativos. Bom, este blog certamente é um, além de me ajudar a organizar meus conhecimentos em OpenSim, que estão muito fragmentados por buscar sempre só o que preciso.

Neste post vou falar sobre o maior interesse dos usuários de OpenSim, módulos de Região. Lembrando o padrão UGAIM, pode-se ainda adicionar mais um servidor, que é a própria região. Ela controla tudo que acontece dentro de si, trocando mensagens diretamente com o cliente, e informando os servidores interessados do que está acontecendo, para garantir coerência com outras regiões e persistência do que acontece, como novos itens no inventário ou novos assets.

Um módulo de região tem praticamente todo o backend to OpenSim a seu dispor para fazer o que quiser. O exemplo clássico, presente na página do OpenSimulator, é o Hello World flutuante. Usando as ferramentas do módulo de região, cria-se as palavras Hello World usando primitives, e faz-se com que essas palavras se movam nos céus baseado em um período de tempo. Não muito útil, mas muito ilustrativo =)

Vamos começar entendendo o que são módulos no OpenSim e do que consistem suas partes. Na wiki, há um guia com o qual me basearei as informações, embora esta página esteja bem desatualizada com o código do OpenSim atual (sim, eu preciso arrumar): http://opensimulator.org/wiki/Getting_Started_with_Region_Modules

Módulos

Módulos são o sangue do OpenSim, e as Interfaces são sua aparência. Baseado nas interfaces, você sabe com o que você tem que encher os seus módulos. Quase tudo no OpenSim é baseado em Interfaces, elas ajudam o projeto a se manter consistente e possibilitar julgamento crítico se o que estamos fazendo faz sentido. Justamente por isso, descobrimos que nosso módulo de região (IRegionModule) estava incompleto, e criamos novas interfaces que serão discutidas a frente. A idéia do OpenSimulator e se tornar 100% dependente de módulos, e ter uma base simples que o torne completamente customizável. Estamos longe da meta, mas melhorando muito. A nova arquitetura já permite que serviços core sejam trocados, podemos ter um novo servidor de Usuário ou de Inventório por exemplo, se você achar que sabe fazer melhor que nós 🙂

Módulos de Região

Mesmo com a mudança de interface dos módulos de região, as antigas ainda são funcionais, portanto tempos 3 interfaces de módulos de região, IRegionModule, ISharedRegionModule e INonSharedRegionModule, sendo que as duas últimas, que são o novo modelo, seguem outra interface, chamada IRegionModuleBase. Como estamos rumando ao futuro, vou pular a antiga interface, e mostrar como funciona a nova.

As duas interfaces, ISharedRegionModule e INonSharedRegionModule são variações do IRegionModuleBase. A única mudança é que definem interfaces para um módulo compartilhado e outro para um módulo não compartilhado.

Um módulo compartilhado é um módulo que é criado uma vez, e todas as regiões compartilham sua instância. Muito útil se o módulo ira trocar informações entre regiões, mas lembre-se que sincronizar seu uso entre várias regiões pode ser complicado também. Um módulo não compartilhado, obviamente, é instanciado um por região. Na antiga interface, isso era só uma propriedade da interface. A razão da separação será explicada adiante.

Um IRegionModuleBase é composto, principalmente, de:

– Initialize(IConfigSource source): Primeiro método executado, logo que o seu módulo é carregado. O parâmetro de entrada é um tipo de arquivo de configuração (usando uma boa IDE, você terá acesso a API e vai entender como funciona, é um sistema simples do tipo dicionário), e representa a configuração do servidor OpenSim (o OpenSim.ini), caso você precise saber alguma configuração, ou até mesmo queira adicionar uma nova, e então procurar por ela aqui. Faça todas as inicializações de variaveis e objetos neste método.

– Post-Initialize(IConfigSource source): Método pertencente somente a interface ISharedRegionModule (dai a diferença). Este método é executado depois que todas as regiões já executaram seus Initialize. Caso haja algo que precise fazer para todas as regiões, mas precisa que as variáveis estejam inicializadas, aqui é o lugar.

– AddRegion(Scene scene): Este método é certamente o mais importante. Neste local você irá receber a instância do object Scene, equivalente a sua região. O objeto Scene (um dos maiores do OpenSim) é responsável pelo controle de tudo que acontece na região. Seja receber novos usuários, avisar outras regiões seu usário está partindo, lidar com scripts, informar a engine de física da existência e proximidade de agentes prims, esse objeto fará tudo o que você precisa. Este método é chamado cada vez que uma nova região é criada, independente de ser um módulo compartilhado ou não.

– RemoveRegion(Scene scene): Exclusivo da nova interface, é até estranho que quem fez a interface antiga não tivesse pensado nela. Ele faz exatamente o que você imagina, ele é ativado cada vez uma região é apagada, informando o módulo da inexistência da região, para que não se depare com módulos tentando realizar ações em regiões que foram apagadas. Para ter noção da importância que isso tem, recentemente corrigi um bug onde se você criasse uma região, apagasse, e criasse outra no mesmo lugar, a nova região estaria impossibilitada de ser acessada até que o servidor fosse reiniciado. Isso porque ao tentar logar em uma região, o cliente avisar o servidor de usuário que por conseqûencia busca informações da região no grid. O grid por sua vez, mantém controle das regiões através de um módulo. Como ele não ficou sabendo em tempo de execução que a região foi embora, ele terá duas instâncias de região no mesmo local, e ele sempre devolve a primeira que encontra. Resumindo, lembre de manter seu módulo informado que a região sumiu para evitar compartamentos estranhos.

– RegionLoaded(Scene scene): Pertence aos dois tipos de interfaces, no entanto faz mais sentido em um deles. É chamado depois que todas as regiões executaram seu AddRegion com sucesso. Em um módulo compartilhado, será chamado várias vezes por instância, enquanto no não compartilhado, será chamado uma vez por instância, logo após o término do AddRegion. Este é o local onde você poderá requisitar interfaces de outros módulos da cena ou pedir um gancho para um evento de outro módulo, pois agora há garantia que todos os módulos já adicionaram esta região e estão funcionais.

– Propriedade Name: Só lembre de preencher esta para que seu módulo tenha um nome ^^

Isso é tudo que precisa saber para desenvolver um módulo. Basta preencher todos esses campos, e seu módulo está pronto! Na próxima edição, revisarei o Hello World atualizado para o novo módulo, e explicarei como fazer o OpenSim entender que há um novo módulo, e executá-lo.

h1

Estrutura do OpenSimulator

julho 31, 2009

Agora que sabemos o que é, a próxima pergunta é como é? Mesmo que o interesse do leitor seja somente contribuir com módulos em cima do OpenSim, e não o desenvolvimento próprio da aplicação, é essencial ter os conceitos bases que formam o servidor do OpenSim, que são os mesmos que formam o Second Life.

O design da Linden é baseado no paradigma chamado de UGAIM; User, Grid, Asset, Inventory e Messaging. Estes 5 servidores são a base de todos os serviços fornecidos pela plataforma, e saber exatamente com qual deles você deseja conversar é essencial para o sucesso de um módulo. Vou descrever brevemente o que são os cinco servidores, me baseando nas definições da wiki, e pretendo abordar detalhes de implementação dos mesmos no futuro, pois há muitos paradigmas que desenvolvedores, principalmente recém-formados, devem desconhecer. O foco será o uso geral do servidor, com explicações rápidas de alguns conceitos. Mas primeiro, alguns conceitos básicos:

Conceitos Básicos

Região: Uma área de 256x256x? metros. Uma região pode ser rodada junto com outros serviços (no caso standalone) ou sozinha (no caso de uma região que deseja se conectar a um grid)

Grid: Conjunto de regiões interligadas, formando uma espécie de mundo. Você pode colocar regiões adjacentes no grid e andar de uma região para outra. Estas regiões podem ser locais uma a outra, ou remotas, o usuário não saberá distinguir.

UUID:  Identificador Único Universal, ver wikipedia para mais detalhes. No caso do OpenSim, usamos o UUID canônica, com 32 dígitos hexadecimais.

Asset: Um asset pode ser traduzido e entendido como um bem, uma propriedade. Pode ir desde uma textura até um objeto inworld, como uma mesa ou um prédio.

Inventório: É um ponteiro para um asset, são referências de objetos que cada pessoa carrega. Por exemplo, você pode carregar com você um prédio inteiro, e irá aparecer na sua lista de inventórios como um item. Se você clicar nele, o simulador irá buscar o item que está sendo referenciado (no caso, o prédio), e irá torná-lo visível no cenário, interagindo fisicamente e visualmente com os usuários.

Prim: Vem dá palavra primitive, ou primitivo. Define objetos no mundo virtual. Um prim pode ser um cubo por exemplo. Ligando prims um com o outro (linked prims), você pode fazer designs complexos, como casas.

Serviços UGAIM

Servidor User: Este é os servidor responsável por autenticar o usuário no grid. O que isto significa é que ele é responsável por uma tarefa muito importante: criar um identificador de sessão para o cliente que será usado para autenticar pedidos aos outros servidores do grid, e associar este identificador de sessão a um UUID. Isso pode também envolver autenticação criptográfica, OpenID, ou ainda a autenticação do nome/senha do residente.

Observações: O servidor de usuários é a ponte entre o cliente e os outros serviços. O cliente NUNCA deve requisitar os outros serviços diretamente. O servidor de usuário é o responsável por autenticar o usuário, perguntar ao servidor Grid detalhes sobre a região que deseja conectar e avisar a região que um cliente está para conectar. No futuro irei revisar o processo de login com mais detalhes. Além disso, o servidor de usuário também fornece informações sobre dos usuários conforme as regiões, serviços ou módulos necessitam.

Servidor Grid: Responsável por autenticar as regiões no grid. Como os grids são bi-dimensionais, é importante configurar corretamente o endereço X,Y de cada região para não haver conflitos. Atualmente, o OpenSim realização uma autenticação de duas vias com servidores de região, baseado em um esquema de shared-secret duplo (chamados “password de entrada” e “password de saída”). Cada região recebe um UUID.

Observações: O servidor de grid é responsável por controlar o grid e fornecer informações das regiões para outros serviços/módulos/regiões. Por exemplo, o grid dá informação da nova região, quando se tenta teleportar ou atravessar de uma região para outra, para o processo saber que região contatar.

Servidor Assets: É essencialmente um banco de dados WRFM (escreve pouco, lê muito).  Quando um asset entra, há duas coisas que se pode falar sobre ele: Um, que tem um UUID como nome e dois, ele entrou para sempre (embora em desenvolvimento futuro do OpenSim, assets não utilizados poderão ser detectados e coletados). Som, textura, imagens, cartões de visita, scripts, objetos de inventório serializados são adicionados e nunca são modificados de novo (são imutáveis). Se decidir que precisa mudar um gráfico 2 pixels para direita de onde está para que sua casa virtual fique mais arrumada, você terá que fazer upload de um novo asset, que terá um novo UUID, e associar o novo UUID com a textura. A antiga ficará para sempre no banco de dados.

Observações: Assets são a vida do OpenSimulator, e por consequência, do Second Life. Tudo que você vê, exceto o terreno e  o céu, são assets. Vão desde um cubo de 1cmx1cm até uma catedral inteira (se não for prims ligados, vários assets). Por essa razão que o gargalo de performance do OpenSim hoje reside no tratamento de pacotes, especialmente de textures, que são assets. Uma região pode ter muitos megabytes de texturas, e quando se tem muitos clientes, é necessário enviar a todos os assets presentes na região. Outro tópico a ser discutido no futuro.

Servidor Inventory: Mas se tudo está misturado na forma de chave-valor de banco de dados, como você consegue organizar e entender os objetos?  O UUID pode ser ótimo para o computador, mas não tem  nenhum nome legível para o ser humano. E como você mantém controle de onde você o  deixou? Este é o trabalho de um outro servidor de banco de dados – um desenhado para muitas pequenas escritas e leituras. Este é o servidor de inventório. Ele funciona ligando UUIDs uns com os outros. O usuário tem um UUID, que é usado para pegar o UUID da sua pasta fonte , que por sua vez possui uma lista de UUIDs que ligam a pastas que também tem UUIDs e o UUID, tipo e nome descritivo de assets. O servidor de inventório retém informação de permissões sobre os itens no inventório.

Observações: Este é o lugar que você vai procurar seus itens que sumiram. Portanto se um item seu desaparecer, relaxe! Lembra do que foi dito no servidor de assets? Nada é apagado. Você apenas pode ter perdido a referência a aquele objeto. É importante saber a diferença desta fronteira para entender o que é um problema de inventório (meu item sumiu) e o que é um problema de asset (meu asset está deformado ou não pode ser encontrado na base de dados).

Servidor Messaging: Este veio depois e não é tão essencial quanto os primeiros quatro. No entanto, se você quer que as pessoas que usam seu simulador possam continuar se comunicando umas com as outras através de qualquer coisa além de prims de letras que vôoam nos céus virtuais (o exemplo na wiki de criação de módulo é escrever Hello World usando blocos, e ficam passeando no céu), você precisa disto. Ele mantém controle de quem deve ouvir o que, mantendo controle de mensagens de longa distância enviadas de um usuário para o outro (uma espécie de SMS), e mantém mensagens diretas não lidas até que elas sejam lidas (também como funciona o SMS).

Observações: Não há muito o que adicionar neste trecho, mas é interessante dar uma exemplo para ver o que se pode fazer com isso. Existe um aplicativo de iPhone que permite que você entre no OpenSim e veja quem está online e consiga enviar mensagens. Isso porque ele se loga no servidor de usuário e depois passa a trocar mensagens com o servidor de Messaging.

h1

OpenSimulator: A Plataforma de Aplicações 3D

julho 22, 2009

Opensimulator_logo200x160Conforme prometido, vou descrever então do que se trata a plataforma do OpenSim. Nas palavras do próprio site:

“OpenSimulator é um servidor de Aplicações 3D. Pode ser usado para criar um ambiente (ou mundo) virtual que pode ser acessado por uma variedade de clientes e protocolos. O OpenSimulator permite que você desenvolva seu ambiente usando a tecnlogia que achar melhor – nós desenhamos o software para ser facilmente extensível através de módulos carregáveis para construir configurações completamente customizadas. OpenSimulator está lançado sobre a licença BSD, tornando-o open source e comercialmente amigável para embutir em produtos.

“Out of the box”, o OpenSimulator pode ser usado para simular um ambiente virtual similar ao Second Life™ (incluindo compatibilidade de cliente). Outros ambientes, protocolos e recursos são suportados via módulos “add-on”. Para uma lista de módulos disponíveis, ver nosso site forge.

Embora o OpenSimulator ainda seja considerado um software alpha, muitas pessoas estão fazendo trabalhos entusiasmantes com ele.”

Com essa descrição e com meu post anterior, tenho certeza que os leitores já são capazes de diferenciar Second Life de Mundos Virtuais como Plataforma 3D. Há inúmeras aplicações que podem ser trazidas para um ambiente 3D que são muito mais naturais que em um ambiente 2D. Como um exemplo clássico, uma coisa que é familiar a nós que temos apresentações e aulas, o quadro negro vs slides.

Com o avanço da tecnologia de PCs e projetores, todos os palestrantes hoje usam slides. No entanto, nós que assistimos a palestras e aulas sentimos uma certa falta do quadro negro e muitas vezes nem sabemos porque. A razão que o quadro negro nos parece mais interessante é a disposição da informação. Enquanto em um slides você é obrigado a focar em um tópico por vez, um quadro negro te dá a perspectiva geral do assunto, e sua mente acha natural buscar o contexto do assunto atual no quadro anterior. O slide nos força a estar 100% imersos no ritmo do palestrantes, o que raramente acontece, e gera o efeito do “slide dá sono”. Em um ambiente 3D, somos capazes de usar slides e ter essa distribuição espacial que é natural a nossa vista.

A meta do OpenSimulator é se destacar completamente do Second Life no futuro, quando for capaz de ser uma plataforma estável e com mais recursos que a Linden pode oferecer (isso já está acontecendo). Alguns exemplos de disposição de informações em um ambiente 3D:

Por fim, não percam a chance de visitar o site do OpenSimulator, olhar os screenshots e tirar um tempo para pensar no que ainda não foi pensado. Sempre ouço as pessoas reclamando que nunca estão na hora certa no lugar certo da inovação, esta é a sua chance de possivelmente criar a nova onda. Pretendo abordar neste blog no futuro diversos conceitos de programação em cima do OpenSim, esclarecer como as coisas funcionam e mostrar para os incrédulos o quanto engenharia de software faz falta em um monstro deste tamanho.

Convido-os também a testar o seu próprio servidor! Ele é realmente “out of the box”, baixe o pacote zippado e rode o executável OpenSim.exe na pasta bin e pronto, basta agora conectar seu SL Viewer ao servidor. Lembrando que para isso você precisa passar um parâmetro a mais para o executável:

1) Windows: Clique o botão direito no link do Second Life, e no campo onde está o caminho do executável adicione -loginuri http://127.0.0.1:9000. Exemplo: “C:\Program Files\SecondLife\SecondLife.exe” -loginuri http://127.0.0.1:9000

2) Linux: Execute o secondlife com o mesmo parâmetro. Exemplo: ./secondlife -loginuri http://127.0.0.1:9000.

Há um outro viewer chamado Hippo Viewer, que é baseado no da Linden mas tem uns tweaks para OpenSim. Um deles é um botão grid, que permite que você salve vários “loginuris”.

h1

Hello Virtual World!

julho 16, 2009

Caros primeiros leitores, eu resisti bravamente por 25 anos mas finalmente tive que ceder: Comecei um blog! Será um grande desafio para mim manter a rotina de estar sempre atualizando este meu novo espaço de expressão, mas como já me engagei com a idéia, acredito que terei sucesso nesta iniciativa.

Gostaria de começar a introdução me direcionando aos que ainda não me conhecem, se é que há pessoas assim visitando este site logo em seu início. Se este é o caso, por favor direcione-se a página sobre para saber mais sobre mim. Muita coisa fará sentido, ou deixará de fazer, quando estiver mais bem informado sobre essa figura.

Para os que me conhecem, a razão pela qual nunca escrevi antes é porque nunca achei que teria assuntos interessantes para movimentar um blog e levantar o interesse das pessoas. Dado meu recente trabalho na área do OpenSimulator, achei que seria relevante ter um blog a respeito deste assunto, novo ainda no Brasil, e que inspira muios erros de interpretação. Quando alguém me pergunta: “O que você faz na IBM? “, sou sempre obrigado a dizer “Sabe SecondLife? Pois é, mais ou menos aquilo”. A razão dessa minha errônea comparação é a falta de conhecimento geral no Brasil a respeito de mundos virtuais. Não os culpo, caros obtusos leitores, que vou perder de cara por ter insultado de obtusos, eu mesmo só tive essa visão após muitos meses de trabalho no projeto. Este primeiro post será sobre esse tópico, abordarei o OpenSim especificamente num tópico futuro.

Mundos Virtuais

IBM VIRTUAL COLLABORATION

Mundos Virtuais são automaticamente associados a Second Life, que por sua vez remete a uma definição imediata: redes sociais. Por isso, as pessoas vem com espadas e nunchakus dizendo que eu não trabalho e que eu só jogo (se eu ganhasse 1c cada vez que eu ouço isso no trabalho. Felizmente a maioria está brincando. Ou não. oO’). A verdade é que rede social é uma aplicação em cima de mundos virtuais. O seu conceito é mais amplo que apenas esta mais famosa funcionalidade, chamada de Second Life. Recentemente ouvi uma comparação que é perfeita para fazer com que todos entendam a idéia: mundos virtuais é o Apache 3D. No início da era web, as pessoas se perguntavam porque tanta importância em cima dessa tal de internet, afinal de contas é só um outdoor digital que você insere dados da sua empresa ou informações pessoais para se divulgar para os poucos geeks que tem acesso. Hoje é óbvio que o Apache é a base de e-commerce, e-business, serviço de pizza, taxi, cinema, bate papo, instant messaging, BLOG, até mesmo um sistema operacional inteiro! Pois é nesse potencial que jaz os mundos virtuais.

Os que me conhecem sabem que eu sou muito realista, e não pretendo deixar de ser nesse caso. Mundos virtuais não é o Apache e acho difícil que seja em um tempo breve, na minha pobre e limitada visão. Mas do mesmo modo que o enxergo de maneira realista e me sinto desanimado com os desafios desta tecnologia (principalmente aceitação do público menos “incluso digitalmente”), eu lembro que um dia o servidor WEB também foi subestimado. Os realistas olhavam para aquilo e nunca imaginavam que iria ser mais do que uma curiosidade tecnológica. Que a internet nunca estaria na casa de todos, seria sempre da “elite técnica”, e por isso nenhuma aplicação em cima dela seria amplamente aceita. Me sinto confortável em saber o quão errado estávamos, e entusiasmado de fazer parte desta nova frente de tecnologia de maneira tão ativa.