Melhorias e Marcadores de Espaço do Banco - Blog dos Desenvolvedores!

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:

  • 1. Método do Old School
  • 2. Deixar 0 do objeto no espaço do banco
  • 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:

  • Remover código que limitava uma conta de avaliação a ter apenas 300 objetos em pilhas (este sistema de avaliação não era usado aproximadamente desde 2012);
  • Remover as restrições de conteúdo antigo (como mundos de Caçador de Recompensas)
  • Reduzir a redundância nos scripts
  • Mover os cálculos somente para exibição para o lado do cliente (por exemplo, o contador de espaços atuais/totais)
  • 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:

  • Equipado
  • Remover um objeto:
  • Há algo que impeça a remoção do objeto? (Área, efeito de status, etc...)
  • Execução de efeitos colaterais (por exemplo, desativação do anel da visibilidade)
  • Equipar um objeto:
  • Você atende aos requisitos para equipar o objeto?
  • Algo impede você de equipar o objeto?
  • Ao ser equipado, ele tenta ocupar um espaço válido?
  • O objeto precisa mudar de tipo? (de novo para usado, etc.)
  • Efeitos secundários de corrida
  • Animal de Carga
  • Ele está atualmente evocado?
  • Este familiar consegue guardar objetos suficientes?
  • O objeto é valioso demais para ser armazenado?
  • É um objeto que não pode ser armazenado?
  • Esta não é uma lista completa, mas é o suficiente para explicar as mudanças. No entanto, elas trouxeram algumas preocupações com o desempenho:

  • Muitas das predefinições para treinamento de habilidades terão a roupa apropriada salva, e a única ação esperada é que o inventário seja mudado a cada vez que a predefinição é carregada. Mas a cada vez que isso acontecia, a roupa vestida era depositada e retirada novamente.
  • As restrições com base em área são realizadas em cada espaço, mas são aplicadas apenas uma vez.
  • É muito improvável que você não atenda aos requisitos, pois precisa ter um objeto equipado para salvá-lo; no entanto, os requisitos podem mudar com as atualizações do jogo.
  • As alterações que eu fiz incluem:

  • Ao tentar esvaziar inventários vazios para carregar uma predefinição, o espaço agora é ignorado se o objeto já estiver na predefinição.
  • Depois de carregar uma predefinição pela primeira vez a cada login, os requisitos são ignorados (a menos que sejam zerados, por exemplo, através de um zeramento de habilidade).
  • As restrições com base em área são verificadas apenas uma vez por carregamento.
  • Vários ajustes foram feitos na forma como os efeitos secundários funcionam.
  • 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):

  • Old School e RuneScape
  • Clicar no inventário para depositá-lo
  • Arrastar do banco para o banco (mudar de espaço)
  • Arrastar do banco para uma aba (criar uma aba)
  • Arrastar do banco para uma aba (mover para a aba)
  • Clicar no banco para retirar para a mochila
  • Apenas OS
  • Arrastar do banco para inserir (mover para antes de um objeto)
  • Apenas RS
  • Arrastar do banco para inserir (mover para antes de um objeto)
  • Arrastar da mochila/objetos equipados/animal de carga para inserir (depositar antes de um objeto)
  • Arrastar do banco para o fim do espaço para largar das abas (mover para o fim do banco)
  • Arrastar da mochila/objetos equipados/animal de carga para o fim do espaço das abas (depositar no fim do banco)
  • Arrastar da mochila/objetos equipados/animal de carga para esvaziar o espaço (depositar)
  • Arrastar da mochila/objetos equipados/animal de carga para espaço ocupado (trocar com inventário que não seja o banco)
  • Arrastar da mochila/objetos equipados/animal de carga para uma aba específica (depositar no fim da aba)
  • Clicar no banco para retirar para os objetos equipados/animal de carga/porta-níqueis
  • Clicar no banco com o botão direito para retirar para os objetos equipados
  • 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:

  • Ciano é um espaço de "inserção"; por exemplo, arrastar algo para ciano 2 inseriria antes do objeto no espaço 2;
  • Amarelo é o espaço de objeto;
  • Vermelho é o fim do "espaço para largar" da aba, para depositar no fim da aba.
  • 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:

  • %bank_tab_2 = 1
  • %bank_tab_3 = 1
  • %bank_tab_4 e superior = 0
  • 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:

  • Arrastar do espaço 1 para o espaço de inserção (ciano) 2 tenta mover o objeto para sua posição atual no banco – para isso, basta remover 1 de %bank_tab_3.
  • Arrastar do espaço 1 para o espaço para largar (vermelho) da aba 2 precisaria adicionar 1 a %bank_tab_2 e remover 1 de %bank_tab_.
  • Arrastar do espaço 2 para o espaço de inserção (ciano) 3 não muda nada, e precisa ser ignorado.
  • Arrastar do espaço 2 para o espaço de inserção (ciano) 4 é simplesmente trocar com o espaço 3.
  • 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:

  • Falha – o objeto não é equipável.
  • Falha – precisa de confirmação antes de equipar.
  • Você já tem o objeto equipado?
  • Êxito – o objeto é empilhável, portanto mais podem ser adicionados.
  • Falha – é o mesmo objeto e não tem dados de aprimoração.
  • Falha – você não tem permissão para equipar o objeto (devido a requisitos, áreas específicas, etc).
  • O objeto é uma arma ou escudo e por isso entra em conflito com os objetos atualmente equipados (por exemplo, ao tentar equipar uma espada de duas mãos com um escudo equipado)?
  • Falha – não estamos retirando o último objeto ou estamos deixando marcadores de espaço e não há espaço suficiente para depositar o objeto extra e/ou a arma que será substituída.
  • Êxito – não foram equipados objetos ou é possível trocar ou retirar normalmente.
  • 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:

  • Ele foi arrastado para uma aba, espaço para inserção, espaço para arrastar ou espaço específico?
  • Ignorar o espaço livre – a não ser que isso fosse exceder o tamanho máximo do banco, em cujo caso deverá ser realizada a compactação e o ajuste do espaço alvo de acordo com os espaços removidos.
  • É necessário um único espaço?
  • Utilizar o espaço livre.
  • São necessários vários espaços (por exemplo, depositar 5 do mesmo objeto aprimorado)?
  • Compactar para garantir que todos os 5 sejam posicionados juntos e colocar 5 espaços na posição do primeiro espaço livre.
  • 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 abas

    Para 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
    Voltar ao início