Introdução
Como anunciamos em maio, os Marcadores de Espaço do Banco estão chegando, e já estão disponíveis nos nossos servidores beta! Gostaria de usar este blog para mostrar um pouco do processo por trás das mudanças técnicas que fizemos.
É o que gosto de chamar de "atualização iceberg". Os marcadores de espaço, a usabilidade e as mudanças na interface ficam à superfície, enquanto a maioria do nosso trabalho fica submerso. Durante vários meses no início do projeto, nem seria possível reparar que houve modificações!
Dê uma olhada nas etapas pelas quais este projeto passou até chegar à fase de lançamento.
Pré-produção
A pré-produção é uma etapa no processo de desenvolvimento que acontece antes de a equipe começar a escrever código. O objetivo é realizar pesquisa, validar e entender melhor qual é o problema.
Abordagens de prototipagem
A primeira etapa para criar um protótipo é analisar várias formas de implementar o conteúdo. Me reuni com os desenvolvedores do mecanismo do jogo para analisar os prós e os contras de cada abordagem, eliminando aquelas que tivessem falhas críticas. Depois de definirmos as principais candidatas, se tornou evidente que tínhamos duas opções:
O método "deixar 0" era o preferido, pois não envolvia criar um montão de novas IDs de objeto (embora isso pudesse ser feito de forma automática) e significava que você só precisava verificar o inventário uma vez ao depositar um objeto. Isso parece ser simples em teoria, mas o que complicava era o fato de o mecanismo do jogo ser desenvolvido com uma rígida regra que dita que um espaço com 0 objetos em um inventário do jogador é um espaço vazio.
Os desenvolvedores tinham uma ideia de quanto conteúdo precisaria ser mudado, mas havia o risco de ser uma estimativa muito baixa. Portanto, eles começaram a criar um protótipo – uma compilação onde apenas as funcionalidades básicas estavam implementadas.
Enquanto eles estavam criando o protótipo, eu assumi a invejável tarefa de transformar o RuneScript para que ele pudesse ser otimizado e ficasse mais fácil de manter, além de deixá-lo pronto para os marcadores de espaço quando o trabalho no mecanismo do jogo estivesse pronto.
Testes automatizados – Produção principal – Conteúdo
Não havia data específica de lançamento para este projeto, pois era extremamente difícil estimar o tempo necessário para concluí-lo.
Minha jornada pelo código do banco passou principalmente por estas etapas:
Otimização
Otimizar um script inclui tudo desde o quão rápido ele é executado, o quão fácil é ler o código e o quão facilmente ele pode ser expandido. Meu principal foco era deixar o código mais rápido sem alterar o comportamento, melhorando simultaneamente a legibilidade.
Verificações iniciais
No estado inicial da otimização, eu verifiquei a lista criada na pré-produção, fiz as alterações que destaquei e garanti que o script de teste passasse pela verificação.
Muito desse trabalho era focado em reduzir código desnecessário, incluindo:
Uma das principais melhorias foi conseguida ao observar a forma como os comandos do mecanismo estavam sendo usados.
Por exemplo: Ao retirar um objeto, precisamos limitar a quantidade de objetos que você pode tentar retirar ao número real que está no seu banco. Isso originalmente usava o comando inv_total (total do inventário), o que parece ser completamente adequado para esta finalidade.
No entanto, no banco todos os objetos são empilháveis, e só permitimos uma pilha de cada objeto. Isso significa que só precisamos levar em consideração o espaço no qual o jogador clicou, exceto ao selecionar para retirar mais do que um de objetos com outros dados (por exemplo, objetos aprimorados), pois eles podem aparecer em vários espaços. De modo semelhante, muito do código original foi escrito antes do RuneScript ter suporte para variáveis locais, portanto essa consulta de inv_total pode ter aparecido em vários locais onde seria executada na totalidade todas as vezes. Por isso, garanti que ela só fosse realizada uma vez.
As predefinições do banco foram onde as maiores mudanças ocorreram. Atualmente, ao carregar uma predefinição, você deposita todos os objetos nos inventários selecionados antes de retirar a predefinição.
Para além disso, há outras considerações:
Esta não é uma lista completa, mas é o suficiente para explicar as mudanças. No entanto, elas trouxeram algumas preocupações com o desempenho:
As alterações que eu fiz incluem:
Tudo isso deve reduzir significativamente o impacto no servidor, embora enquanto jogador você normalmente não deva estar ciente de que algo tenha mudado – a não ser que algo dê errado!
Capacidade de manutenção
Depois da primeira manutenção, meu objetivo era tornar o código mais fácil de atualizar e seguir. Isso significava reescrever grande parte dele, particularmente a forma como as abas do banco funcionam no código.
As seguintes são as principais operações que o banco suporta (o que me deu inveja das limitadas funcionalidades de arrastar do Old School):
Todas essas operações de arrastar adicionam bastante complexidade ao código, particularmente para objetos equipados e nos animais de carga, onde se aplicam regras especiais. Mas antes de passar para essa parte, vamos dar uma olhada em como a interface e as abas são geradas.
Nesta imagem:
Isso também destaca como as abas funcionam – o que pode parecer anti-intuitivo. Os primeiros espaços do banco são os que estão em abas, e tudo o que não esteja vem depois, mas é renderizado primeiro. Uma aba é apenas uma variável que conta quantos espaços cada aba tem. Neste banco:
Isso significa que as abas começam a funcionar como uma pirâmide; se imaginarmos que há mais algumas abas, fica como na tabela abaixo:
Aba | Espaço Inicial | Espaço Final |
---|---|---|
2 | 0 | %bank_tab_2 |
3 | %bank_tab_2 | %bank_tab_2 + %bank_tab_3 |
4 | %bank_tab_2 + %bank_tab_3 | %bank_tab_2 + %bank_tab_3 + %bank_tab_4 |
5 | %bank_tab_2 + %bank_tab_3 + %bank_tab_4 | %bank_tab_2 + %bank_tab_3 + %bank_tab_4 + %bank_tab_5 |
6-15 | Seguem o mesmo padrão | |
Não em abas | Total ao adicionar _2 até _15 | Último espaço usado do banco |
Mesmo nesta simples imagem, há alguns casos especiais para tratar, como por exemplo:
A maioria das outras combinações significaria mudar a ordem dos objetos. Por exemplo, arrastar o espaço 1 para o espaço para largar (vermelho) na exibição "Todos" moveria o objeto para o espaço 5 e deixaria um espaço em branco na aba 3, enquanto que na versão atual do jogo isso excluiria a aba 3.
Outra coisa que isso afeta é a forma como os scripts de depósito lidam com as abas. Atualmente, eles depositam no fim do banco (garantindo que haja espaço suficiente no banco para o objeto) e, se essa operação for concluída com êxito, eles tentam mover novamente para o fim da aba alvo. Isso leva de volta à seção de otimização, onde se você pegar um banco praticamente cheio, será realizada verificação em 1200 espaços (para encontrar o primeiro espaço livre disponível e garantir que ele não esteja já no banco), e em seguida você pode estar movimentando os objetos para movê-lo de volta para o espaço 600.
Um problema com isso é que, no desenvolvimento, quem for usar o script de "depositar" terá que se lembrar de verificar o sucesso e chamar o script "mover para aba". Minhas mudanças aqui foram para mudar os objetos de lugar para esvaziar o espaço alvo antes de depositar (em vez de depois), eliminando totalmente a segunda execução.
Outras mudanças incluíram tornar as verificações de retirada mais centralizadas, especialmente no inventário de objetos equipados. Para depositar um objeto no banco (e, de forma semelhante, para retirar dele) usaríamos o script ~deposit_genetic, mas para objetos equipados, temos que usar ~removeobj, que por sua vez não tem um nome adequado. A árvore de decisões para esta situação fica bastante complexa.
Ao retirar objetos do banco e equipá-los diretamente, estes são alguns dos estados possíveis de falha/êxito:
Não parece ser algo muito complicado quando simplificamos desta forma, mas é muito mais complicado no código! Os animais de carga têm ainda mais cenários de falha, como a quantidade de objetos que podem ser retirados sendo limitada por valor.
Mudanças
Depois das melhorias essenciais, era hora de fazer mudanças na funcionalidade.
Remover a compactação
A mudança principal era remover a compactação ou movimentação ao retirar – isso causava muitos problemas ao clicar várias vezes para retirar objetos e acidentalmente mudar as abas de lugar, ou ao retirar o objeto errado porque ele esvazia uma fila e puxa os objetos no banco para cima. No passado, tentamos mitigar o impacto disso, mas vai ser sempre um problema, pois o cliente e o servidor precisam ser sincronizados conforme o banco muda. As mudanças aqui significam que ao retirar um espaço, ele permanecerá como um espaço vazio até que você feche seu banco ou que ele precise ser usado, efetivamente eliminando a movimentação exceto em caso de exibição de filtro ou pesquisa.
Isso não é uma mudança tão simples quanto aparenta: os códigos externos ainda precisam movimentar os espaços para baixo, e as mudanças de tamanho da aba precisam ser calculadas todas de uma vez, em vez de acontecer em tempo real.
O cálculo que determina em que espaço depositar o objeto também ficou mais complexo. Presumindo que ele não esteja no banco:
Marcadores de espaço
A principal parte do suporte para marcadores de espaço foi trabalho no mecanismo do jogo, portanto para mim, adicionar a funcionalidade pareceu estranhamente fácil. No entanto, isso foi devido a meses de ajustes de manutenção e otimização!
A maior parte da complexidade ocorreu devido a scripts externos. Vamos usar como um exemplo bastante específico a fermentação do suco de uva – temos um banco completamente cheio de suco de uva não fermentado, sem suco de uva "normal". No estado atual, o processo excluiria o suco de uva não fermentado e adicionaria novamente o suco de uva pronto. Isso é um método seguro, pois é garantido que o espaço no banco estará vazio. Com os marcadores de espaço, não será mais assim, pois será possível deixar uma pilha de zero suco de uva não fermentado no banco. Podemos ignorar a configuração do marcador de espaço aqui para garantir que ele permaneça vazio, mas a mesma função para remover os objetos também é usada em locais onde você esperaria que os marcadores de espaço funcionassem – como um mordomo obtendo tábuas do seu banco.
A única solução, portanto, foi verificar as referências à função de exclusão e fazer cada situação decidir se respeitaria os marcadores de espaço ou não. Também aproveitei esta oportunidade para mudar a forma como várias dessas funções de "excluir depois adicionar" estavam estruturadas. O motivo por trás dessa função de "excluir depois adicionar" era que se você simplesmente mudasse o espaço para o novo tipo de objeto, você potencialmente teria espaços duplicados, e eu escrevi uma nova função para lidar com isso e ao mesmo tempo mudar o tipo de espaço.
Também fiz o mesmo para vários outros comandos.
Adicionar abasPara adicionar outras abas, também reescrevi muito do sistema de abas. Todos estavam usando componentes estáticos (definidos no editor de interfaces) e agora utilizam componentes dinâmicos (criados no script).
No editor de interfaces, a versão atual teria a seguinte aparência:
Enquanto que no beta, terá esta:
Para quem não conhece a ferramenta, é normal que pareça um pouco assustador. Para descrever rapidamente, as seções verdes (Camadas) são contêineres que contém outras coisas (incluindo componentes gerados por script), as laranjas (Retângulo) são usadas simplesmente como linhas divisórias (mover a ordem delas tem um grande impacto no visual da aba selecionada) e as azuis (Gráfico) são o ícone da aba.
Isso significa que para adicionar novas abas, não precisamos editar o arquivo da interface (que é difícil de mesclar na compilação) e copiar e colar todas as camadas exibidas em cada guia. O arquivo com aproximadamente 200 linhas de código com remapeamento de IDs para componentes também é removido, para que seja muito mais fácil de atualizar – quer seja novas abas, elementos gráficos, mudanças em ícones ou outros. Os componentes do "espaço para largar da aba" mencionado anteriormente também foram mudados da mesma forma.
Resumindo, este é só um exemplo do quão complicadas essas coisas podem ser e de por que elas demoram tanto!
Divirta-se com o beta!
Mod Hunter
Desenvolvedor Técnico Principal