Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Mayheim
Valheim como MMORPG: level e atributos, fome no lugar da comida, cidades de bioma dentro de salas de cripta construidas a partir de um MOLDE feito no jogo, mercador com tabela de precos por bioma, missoes, guardas, bosses em toda dungeon e chefes mundiais.
CHANGELOG
Changelog
2.0.21 — 2026-10-07
- Jogo congelando na morte de chefe de nível alto:
- O ouro do chefe entrava no loot como uma entrada só, e o Valheim cria um objeto com física por unidade. Num chefe com milhares de moedas, nasciam milhares de moedas no mesmo ponto, a física explodia e o jogo ia a quase zero quadros por segundo.
- Agora todo item que empilha cai em pilhas (até o máximo de cada pilha). Exemplo: 4.500 moedas viram 5 pilhas. Nenhuma moeda se perde.
- Novas dependências:
- Mayheim - Helheim Expedition 0.14.0, a expansão do anel externo (tiers 9 a 12), agora instalada junto.
- Míticas 0.1.3, com o novo brilho de chamas nas relíquias.
Atenção: a DLL mudou. Servidor e jogadores precisam estar na 2.0.21, com o Helheim 0.14.0 e o Míticas 0.1.3.
2.0.20 — 2026-09-29
- Fim do "invocado por trapaça":
- O Valheim marcava como trapaça itens vindos do console, drops de criaturas feridas em modo deus, fantasma ou voo de debug (ou por quem estava com arma marcada) e recursos de peças construídas sem custo. A marca se espalhava por caça, baú e troca.
- Agora nada passa a sair marcado, e o que já estava marcado perde a marca ao carregar: mochila, baú e item no chão.
- A marca só bloqueava as conquistas da Steam.
- Config:
[39 - Trapaca] RemoverMarca(ligada).
Atenção: a DLL mudou. Servidor e jogadores precisam estar na 2.0.20.
2.0.19 — 2026-09-29
- Portal da cidade dentro da pedra:
- Dentro da sala da cidade, o portal media o chão pelo terreno do mundo, 3500 m abaixo, e acabava nascendo dentro da rocha.
- Quem atravessava o portal do spawn não achava chão do outro lado, e o jogo o soltava no terreno de baixo, no rio em frente à Cidadela.
- Agora o portal da sala procura uma vaga com piso da sala sob ele e na frente dele. Sem vaga boa, fica no centro da sala.
- Novo comando de admin
mmo_city portal_spawn mover <tier>: tira o portal do spawn daquele tier e põe o novo onde você está, sem mexer na cidade. Serve para quando ele nasceu bugado. mmo_city refundarvolta a ser como antes: ele não mexe mais no portal do spawn.
Atenção: a DLL mudou. Servidor e jogadores precisam estar na 2.0.19.
2.0.18 — 2026-09-28
Cidadela Infestada de volta
A entrada da Cidadela da Rainha mostrava "Bloqueado". A cidade da Mistlands tinha sido construída dentro da masmorra da Rainha: as cidades de bioma ficavam 5000 m acima do altar do chefe, a mesma altura em que o jogo põe as masmorras. Na Mistlands, o "altar" é a própria entrada da cidadela, e a limpeza feita antes de construir a cidade apagou a masmorra.
- As cidades de bioma agora ficam a 3500 m, longe de qualquer masmorra do jogo.
- A limpeza antes de construir a cidade só remove o que é do Mayheim. Masmorra, baú e criatura do jogo nunca mais são apagados.
- Novo comando de admin
mmo_city restaurar <tier>, para usar perto da entrada do chefe (6 = Rainha, 8 = Norte):- tira a cidade de dentro da masmorra;
- gera a masmorra de novo, igual à original (mesma semente), sem duplicar o que sobrou;
- a cidade renasce no lugar novo.
Portal do spawn onde você está
mmo_city refundar <tier>agora tira o portal do spawn daquele tier e põe o novo na posição do seu personagem. A refundação da cidade não cria outro.
Atenção: a DLL mudou. Servidor e jogadores precisam estar na 2.0.18.
2.0.17 — 2026-09-28
Loot no lugar do ouro
- Mobs e bots não largam mais ouro. Um lobo não carrega moedas. Isso vale para o ouro do Mayheim e para as moedas que o jogo base dava ao Troll e aos Dvergers.
- Fulings continuam largando ouro (goblin → ouro), e chefes também: de dungeon, mundiais e os do jogo.
- O loot das criaturas tem preço de venda próprio no mercador, escalado por bioma e dificuldade:
| Bioma | Exemplos (moedas por unidade) |
|---|---|
| Prados | pele de cervo 5, troféu de cervo 75 |
| Floresta Negra | pele de Troll 40, troféu de Troll 300 |
| Pântano | entranhas 20, corrente 80, troféu de abominação 1.000 |
| Montanha | couro de lobo 75, presa de lobo 100, cristal 100 |
| Planícies | pedaço de metal negro 120, peliça de Lox 150 |
| Névoa | carapaça 350, mandíbula 500, bolsa de bile 600 |
| Cinzas | pele de Ask 400, coração de Morgen 1.000 |
| Norte Profundo | pele de alce 600, nornafio 2.500 |
- Matéria-prima da tabela também é vendável: amora do Ártico, cevada, linho, madeira de Yggdrasil, seiva, gemas das Cinzas...
- Sem revenda com lucro: se o mercador também vende o item (troféus), ele paga no máximo 80% do próprio preço.
- As peças de arma e armadura que os mobs largam continuam caindo e continuam vendáveis.
- Config:
[14 - Mercador] PrecosVenda(tabela) eTetoVendaSobrePrecoDaLoja;[9 - Drop de equipamento] OuroSoFulingsEChefesePrefixosFuling.
Atenção: a DLL mudou. Servidor e jogadores precisam estar na 2.0.17.
2.0.16 — 2026-09-27
- Descrição das refeições de combate: a raridade e os efeitos agora também aparecem na tela de receitas (caldeirão, forno, mesa de preparo), e não só no item da mochila.
Esta versão inclui as Refeições de combate (2.0.14) e o Vigia de teleporte (2.0.15).
Atenção: a DLL mudou. Servidor e jogadores precisam estar na 2.0.16.
2.0.15 — 2026-09-27
Portal preso na tela escura
Às vezes o portal ficava para sempre na animação. O jogo base espera a área de destino carregar sem limite de tempo, e um único objeto que não chegasse a ser criado prendia o jogador ali, sem nada no log.
- Depois de 15 segundos preso, o log registra o que falta no destino: se o terreno carregou e quais objetos ainda não foram criados, pelo nome. Assim, na próxima vez, dá para saber a causa.
- Depois de 45 segundos preso, o teleporte é concluído no chão do destino. Se o terreno do destino ainda não existir, o vigia não solta o jogador no vazio: ele continua esperando e o log explica.
- Ajustável na config
[37 - Vigia de teleporte].
Esta versão inclui as Refeições de combate da 2.0.14.
Atenção: a DLL mudou. Servidor e jogadores precisam estar na 2.0.15.
2.0.14 — 2026-09-27
Refeições de combate
As comidas especiais agora dão, além da saciedade, um buff de combate. Cada uma tem uma função (dano, defesa, magia, mobilidade), e as receitas de biomas novos ficam mais especializadas, sem simplesmente substituir as antigas.
- Uma refeição por vez: comer outra comida de combate troca o buff. Comida simples só enche a barriga e não mexe no buff.
- Dura 30 minutos, com ícone e cronômetro na barra de status. Dá para comer mesmo com a barriga cheia, para trocar ou renovar o buff.
- Relogar mantém o buff. Morrer tira o buff, menos numa corrida mítica.
- O tooltip da comida mostra a raridade e os efeitos.
| Comida | Raridade | Efeitos |
|---|---|---|
| Torta de carne de Lox | Raro | +5% dano físico |
| Pudim de sangue | Raro | +8% regeneração de stamina |
| Pão | Raro | +3% dano físico, −3% dano recebido |
| Espetinho de lobo | Épico | +8% dano físico, −5% stamina por ataque |
| Sorvetolho | Épico | +10% dano mágico, +5% regeneração de eitr |
| Cozido de serpente | Épico | −5% dano recebido |
| Lebre suprema da névoa | Lendário | +12% dano físico, +10% atordoamento |
| Prato de carne | Lendário | −10% dano recebido, +5% regeneração de vida |
| Salada | Lendário | +8% stamina máxima, +5% velocidade, +5% regeneração de stamina |
| Geleia de buscador | Lendário | +15% dano mágico, +10% regeneração de eitr, −10% eitr por ataque |
| Torta picante | Lendário+ | +15% dano físico, −10% stamina por ataque, +5% dano recebido |
| Mistura escaldante | Lendário+ | +10% dano de fogo, −15% dano de fogo recebido, −5% dano recebido |
| Purê de carne | Lendário+ | −15% dano recebido, +10% vida máxima |
| Torta de crosta assada | Lendário+ | +10% todo dano, −10% dano recebido, +5% stamina máxima |
| Almôndegas e batáters | Mítico | +15% vida máxima, +10% stamina máxima |
| Sopa de carne de foca | Mítico | −30% dano de gelo recebido, −10% dano recebido |
| Carne no pão | Mítico | +12% dano físico, −8% dano recebido |
| Suco de arando | Mítico | +20% dano mágico, +10% regeneração de eitr, +10% eitr máximo |
- Fora do mercador: essas comidas não são vendidas pelo mercador e ele não as compra, como as chaves míticas. Continuam livres para cozinhar e trocar entre jogadores.
- Tudo fica na config (
[36 - Comidas de combate]), então o servidor pode rebalancear sem atualização.
Atenção: a DLL mudou. Servidor e jogadores precisam estar na 2.0.14.
2.0.13 — 2026-09-26
- Venda vai direto para o cofre. O ouro de tudo o que você vende ao mercador entra no cofre, e não mais na mochila. Acabou a venda recusada por falta de espaço para as moedas. O cofre já paga compras, reparos e melhorias.
- O topo do mercador mostra as moedas da mochila e o saldo do cofre.
- Com o cofre desligado na config (
BankEnabled = false), a venda continua pagando na mochila.
Atenção: a DLL mudou. Servidor e jogadores precisam estar na 2.0.13.
2.0.12 — 2026-09-26
- A dependência do Mayheim Míticas sobe para a 0.1.2:
- a relíquia sorteia primeiro o tipo e depois o item, e puxa para o estilo de quem recebe (mago tende a ganhar cajado e armadura de mago, arqueiro tende a ganhar arco ou besta);
- cajado relíquia gasta menos eitr, no lugar da infusão elemental.
- Nada mais mudou: a DLL do Mayheim é a mesma da 2.0.9.
2.0.11 — 2026-09-26
- A dependência do Mayheim Míticas sobe para a 0.1.1, que corrige o Chefe Mítico que podia nascer preso na rocha das cavernas de gelo e traz o comando de admin
myth_chefe. - Nada mais mudou: a DLL do Mayheim é a mesma da 2.0.9 e da 2.0.10.
2.0.10 — 2026-09-25
Dungeons Míticas
O Mayheim passa a trazer o Mayheim Míticas:
- Chaves Míticas caem dos chefes;
- usada na entrada de uma dungeon do bioma, a chave refaz a dungeon inteira, desperta um Chefe Mítico e deixa os monstros mais fortes a cada nível;
- quem derruba o chefe leva relíquias, equipamento do bioma com poder, infusão elemental permanente e atributos extras.
Quem atualizar pelo mod manager recebe o Míticas automaticamente. Ele é um mod à parte e também funciona sem o Mayheim.
MayheimTrader 0.1.1
A dependência do MayheimTrader sobe para a 0.1.1, a versão que o servidor usa. Antes, quem instalava do zero recebia a 0.1.0 e era recusado na conexão.
Nada mais mudou
A DLL do Mayheim é a mesma da 2.0.9: esta versão só atualiza as dependências. Servidor e jogadores na 2.0.9 continuam compatíveis.
2.0.9 — 2026-09-25
PvP sempre ligado
O PvP agora vale o tempo todo, para todo mundo. O botão de PvP do inventário fica marcado e travado. Continuam sem se ferir:
- jogadores da mesma facção;
- membros do mesmo grupo, com ou sem facção.
Sem facção não é mais fora da guerra
Quem não escolheu lado passa a ser alvo dos bots das duas facções, e também pode ser escolhido como alvo quando um bot sai em ataque.
Karma na guerra de facções
Matar um jogador da facção inimiga não soma karma, nem quando o golpe final vem de longe (flecha, cajado). Antes, se o assassino não estivesse carregado perto da vítima, o karma escapava e era somado.
Grupo: o convite funciona de primeira
Corrigido o convite que só funcionava na segunda tentativa. Ao convidar, o jogo criava um grupo só com você, e a verificação automática desfazia esse grupo em até 5 segundos. Quem aceitasse depois disso não entrava. Agora o grupo nasce quando o convite é aceito, e o convite vale por 60 segundos. Se não der para entrar (grupo cheio ou convite vencido), quem aceitou recebe um aviso.
Casa de Leilões removida
A Casa de Leilões saiu do mod. Para trocar itens entre jogadores, use o MayheimTrader, que já vem junto desde a 2.0.8.
O baú do leilão some sozinho da Vila do Vale na primeira vez que alguém passa por lá depois de atualizar. O que ainda estava anunciado nele fica registrado no log do servidor. O comando mmo_auction, a seção 25 - Mercado da configuração e as placas Leilão nos moldes de cidade deixam de ter efeito.
Comando /party
No chat: /party NomeDoJogador convida pelo nome, sem limite de distância. Se essa pessoa já tinha te convidado, o comando aceita o convite dela. Serve só o começo do nome, desde que só um jogador online comece assim.
2.0.8 — 2026-09-24
Troca entre jogadores
O Mayheim passa a trazer o MayheimTrader: troca segura entre vikings, com as duas ofertas lado a lado, confirmação dos dois lados e os itens guardados pelo servidor até o acordo ser selado. Nada se perde, nada se duplica.
Quem atualizar pelo mod manager recebe o MayheimTrader automaticamente — ele agora é uma dependência declarada, como as Adagas Cruzadas.
BepInEx alinhado
O Mayheim pedia o BepInExPack 5.4.2333, enquanto as Adagas Cruzadas e o MayheimTrader pedem o 5.4.2350. Na prática o mod manager já instalava o 2350, porque os outros dois exigem — o manifest só passou a dizer a verdade.
2.0.7 — 2026-09-24
Cada mercador vende as ferramentas do seu bioma
A regra é o bioma do material, que é como o jogo progride: a picareta de metal negro é das Planícies porque o metal negro é das Planícies.
O mercador das Planícies não vendia a picareta de metal negro — e o do Pântano também não vendia a de ferro. As duas tinham sido classificadas num tier errado pela classificação automática antiga, e na hora de escrever a tabela de preços acabaram fora de todos os mercadores.
| Mercador | Novo à venda |
|---|---|
| Campos | Machado de pedra (100), Machado de sílex (200), Vara de pesca (350) |
| Floresta Negra | Cultivador (800), Quebragamos (1.300) |
| Pântano | Picareta de ferro (3.300), Marreta de ferro (4.200) |
| Montanha | Foice (5.000) |
| Planícies | Picareta de metal negro (13.300) |
| Mistlands | Tocha da névoa (300) |
Flechas vendidas de 100 em 100
Munição vinha no mesmo lote dos materiais, 10 por compra — encher uma aljava pedia dez cliques. Agora flechas, virotes e projéteis têm lote próprio de 100. O preço por unidade não mudou; o total por compra é que fica dez vezes maior.
2.0.6 — 2026-09-23
O ouro do cofre paga direto, sem passar pela mochila
A 2.0.5 deixava comprar com o ouro guardado, mas por um caminho errado: ela trazia as moedas do cofre para a mochila e deixava a loja cobrar dali. Isso herdava o teto de carga.
A conta: moeda empilha de 999 em 999 e a mochila tem cerca de 32 espaços — algo perto de 32 mil no total. A tabela do mercador tem item de 85 mil. Ou seja, os itens mais caros do jogo continuavam inalcançáveis mesmo com o ouro no cofre, que é exatamente o que o cofre existe para resolver.
Agora a cobrança é interceptada no momento da compra: a mochila entrega o que tem, o cofre cobre a diferença, e o jogo recebe um pedido reduzido ao que a mochila realmente possui. Nenhuma moeda é movida.
Vale para a lista da loja e também para reparo, melhoria e os itens do mod, que passaram a usar a mesma cobrança.
2.0.5 — 2026-09-22
O cofre do mercador virou um cofre de OURO, sem limite
Ele era um inventário de 48 slots e aceitava qualquer coisa. Agora guarda só ouro, e guarda sem limite de quantidade — 100 mil, 200 mil, o quanto for.
A mudança parece pequena e não é: o cofre deixou de ser um inventário e passou a ser um número. Não há pilha, não há grid, não há slot. E isso remove pela raiz a fragilidade que rondava o ouro: um inventário serializado é reconstruído item a item no carregamento, e cada item é recriado a partir do prefab e aparado pelo limite de pilha daquele instante. Número não se apara.
Dá para comprar direto com o ouro do cofre. O saldo que a loja mostra, o botão Comprar e a verificação de preço passam a contar mochila + cofre; na hora de pagar, o que falta na mochila vem do cofre sozinho. Vale também para reparo, melhoria e os itens do mod.
O que você guardou antes não se perde. Quem tinha itens no cofre antigo encontra uma lista de resgate dentro da aba COFRE, somente para retirada. O ouro que estava lá migra sozinho para o número. A lista some quando você esvazia, e nada é apagado antes disso.
Um defeito de perda de ouro, encontrado pelo teste
Escrevendo os testes do cofre, um caso falhou de um jeito que valia mais que o próprio caso: 900 moedas gravadas, zero lidas de volta.
A causa: moeda criada a partir de uma cópia do modelo do item nasce sem a referência ao prefab, e é justamente dessa referência que a gravação tira o nome do item. Gravava vazio, e no carregamento a moeda não voltava. Quem visse isso em jogo veria ouro sumindo ao deslogar — exatamente o sintoma relatado no servidor.
Toda entrega de moeda passa a ser feita pelo caminho que o próprio jogo usa, que resolve o prefab e preenche essa referência.
Compra com a mochila cheia não quebra mais
O BuySelectedItem do jogo base usa o retorno de AddItem sem conferir nulo: comprar sem espaço estourava NullReferenceException e podia cobrar sem entregar. Agora a compra é recusada antes, com mensagem.
2.0.4 — 2026-09-21
Os chefes do jogo escalam menos em grupo
A vida do chefe subia 60% por jogador extra no raio de 80 m. Isso foi pensado para os chefes de dungeon, os chefes mundiais e os guardas de cidade, que são invenção do mod e foram desenhados em cima desse número — mas o mesmo valor pegava também Eikthyr, Elder, Bonemass, Moder, Yagluth e a Rainha, que o jogo base já desenha para grupo. Somar 60% por cabeça em cima disso virava parede de vida: cinco jogadores enfrentavam 340%.
Os chefes principais passam a ter número próprio: +15% por jogador extra.
| Jogadores | Antes | Agora |
|---|---|---|
| 2 | 160% | 115% |
| 3 | 220% | 130% |
| 5 | 340% | 160% |
| 10 | 640% | 235% |
Chefe de dungeon, chefe mundial e guarda de cidade continuam em 60% — nada muda para eles. O valor novo é ScalePerPlayerVanillaBoss em [16 - Dungeons], e o log passa a dizer qual das duas regras pegou em cada luta.
2.0.3 — 2026-09-21
As Adagas Cruzadas voltam a ser um mod separado
Elas estavam dentro do Mayheim desde 12/09, no estágio 0.1.0: as oito adagas e mais nada. O mod próprio seguiu evoluindo e chegou ao 0.3.0 com um sistema que o Mayheim não tinha como acompanhar por cópia — 8 resinas de infusão, bônus pela perícia de Facas, aura na lâmina, ícone de status no inimigo, som por resina.
Manter duas cópias do mesmo código, uma sempre atrasada, é pior que depender do original. Então a cópia saiu, e o MayheimDEV-AdagasCruzadas entra como dependência declarada.
O que isso muda na prática:
- As adagas que você já tem continuam suas. Os prefabs são os mesmos (
AdagasCruzadas_KnifeFlint_Duale companhia), então nada some de inventário, baú ou leilão. - Agora é obrigatório ter os dois mods. O AdagasCruzadas 0.3.0 tem checagem de versão própria na conexão: quem não tiver, ou tiver versão diferente, é recusado pelo servidor.
- A integração com o Mayheim fica: o mercador continua vendendo cada adaga dupla pelo preço da faca base, no bioma dela, e o ItemGuard continua deixando todas passarem.
Condicionamento passa a dar só capacidade de peso
O atributo abria duas coisas ao mesmo tempo: mais peso carregado e mais linhas de mochila (8 slots a cada 96 kg acima de 300). Cada ponto gasto pagava dois prêmios, e o segundo mudava o jogo mais do que se pretendia — carregar mais é uma coisa, ter onde guardar é outra.
Agora o Condicionamento dá só o peso: 4 kg por ponto, como sempre deu. O inventário fica no tamanho do jogo base.
Ninguém perde item: a mochila só encolhe depois que as últimas linhas estiverem vazias, e até lá o aviso pede para esvaziá-las.
O sistema continua inteiro no código, desligado por padrão em [13 - Slots por peso], para quem quiser ligar de novo.
Refundar cidade agora apaga mesmo os portais dela
RemoveCidade achava as coisas por ZDO marcado com o tier e por proximidade de 150 m do centro. Os dois portais do tier não moram perto da cidade: o do respawn fica nas pedras iniciais e o do chefe ao lado do altar, a milhares de metros. Nenhuma das duas varreduras os alcançava.
O efeito era pior que sujeira: LiberaPortal desiste quando já encontra um portal com a etiqueta, então a cidade refundada nunca ganhava portal novo — o velho ficava para sempre.
Agora a varredura é pela etiqueta (o ID da cidade), sem filtro de distância, e por ZDO — que é o que faz funcionar no servidor dedicado, onde as zonas são ghost e a cena está vazia.
Portal não nasce mais cravado em pedra
O molde guarda um deslocamento em relação às pedras iniciais, e esse deslocamento é igual em todo mundo — o relevo não é. Um dos portais do respawn nasceu dentro de uma pedra do círculo.
A primeira tentativa de conserto falhou por um detalhe que vale registrar: as pedras grandes e estáticas do jogo ficam na camada terrain, não em static_solid. A checagem excluía terrain de propósito, para o chão plano não acusar colisão em todo ponto, e com isso ficou cega justamente para o único obstáculo que importava.
Agora são três coisas:
terrainentra na máscara e a caixa de teste sobe: vai de 1 m a 2,6 m acima do chão, então solo plano passa por baixo e pedra acusa.- A caixa tem a largura real do portal (2,2 m); a estreita passava ao lado da pedra.
- Teste de planicidade por amostragem de altura, que não depende de colisor nenhum e por isso vale mesmo se a zona ainda não terminou de carregar.
Se o lugar estiver ocupado, o portal procura vaga em 8 direções e vira de frente para as pedras; se nenhuma servir, nasce no lugar do molde e o log diz por quê.
2.0.2 — 2026-09-18
O raid de boss virou agendamento, como o chefe mundial
Medido no servidor: 40 dias de mundo, Eikthyr e o Ancião derrotados, nenhum raid de boss — e o mmo_chaves confirmou que a chave de cooldown nunca chegou a existir, ou seja, nenhum deles jamais começou.
A causa eram duas loterias em série. O RandEventSystem só considera um evento quando há jogador parado dentro de uma base no instante do sorteio, e mesmo aí o raid de boss disputa com todos os raids do jogo base. Num servidor onde as pessoas exploram, lutam em dungeon ou estão dentro da sala da cidade, quase nunca havia candidato válido.
Agora a regra é a mesma do chefe mundial, que sempre funcionou porque é relógio e não sorteio:
- Uma data no mundo, na chave global
mmo_bossraid_next <dia>, empurrada emCooldownDaysa cada raid. - Chegado o dia, o servidor escolhe um jogador conectado ao acaso.
- O chefe convocado é o do bioma onde esse jogador está — e só se ele já tiver sido derrotado. Se ainda não foi, nada acontece e o próximo tique tenta de novo; a data não é gasta.
- Um de cada vez: enquanto o raid anterior estiver de pé, nenhum outro começa.
Os clones saíram do sorteio (m_random = false), então não competem mais com os raids do jogo base — nem os atrapalham.
O resto do raid não mudou: a horda original do bioma vem junto com o chefe, o evento termina quando ele morre, e forçar um pela mão (event mmo_bossraid_...) continua funcionando e passa a contar como o raid do período.
2.0.1 — 2026-09-18
Correções do primeiro dia da 2.0.0 rodando no servidor dedicado. Todas do mesmo tipo: coisa que funciona quando você é o host e não funciona quando você é cliente de um servidor dedicado.
Os NPCs da vila do spawn não nasciam
O servidor montava a vila a partir do molde, e para isso precisava ler a cota do terreno das pedras iniciais. Num servidor dedicado as zonas são ghost — os ZDOs existem, os GameObjects não —, então não há colisor de terreno e o raio nunca achava chão. O tique tentava de novo a cada 20 s, para sempre, em silêncio.
Agora o servidor delega a construção ao jogador mais próximo do spawn, que é onde todo mundo entra (mesmo padrão que o chefe de mundo já usava).
A cidade de bioma nascia sem sala e sem portal
A fundação também é delegada a um cliente, e do lado dele o código reconsultava a posição do altar em ZoneSystem.m_locationInstances — uma lista que só existe no servidor. A consulta devolvia nada e a cidade morria ali. A posição agora viaja junto com o pedido.
O mesmo vale para o portal ao lado do altar do chefe, que dependia da mesma consulta.
mmo_city ir e conferir eram recusados no cliente
Uma guarda no topo do comando só deixava refundar passar. Sem ir, o admin não tinha como chegar ao altar — e sem chegar ao altar não há como refundar cidade nenhuma, porque a fundação é por proximidade. Os dois passam agora: conferir roda local (o centro da cidade vem de chave global, que o cliente tem) e ir <tier> pergunta o destino ao servidor, que confere a adminlist antes de responder.
Poção custava cinco vezes o preço anunciado
Consumível vai para a loja em lote de 5 e m_price é o preço do lote: a poção menor de 300 aparecia por 1500. Agora o valor definido é o do pacote inteiro — 5 poções menores por 300, 5 de vida por 600, 5 grandes por 800, 5 de vigor por 500, 5 de eitr por 500. O mercador passa a avaliar a unidade em 1/5 disso na recompra, senão revender o pacote devolveria o preço cheio.
2.0.0 — 2026-09-18
Versão que exige wipe. O sistema de cidades foi refeito de baixo para cima e a economia do mercador passou a ser uma tabela escrita à mão. Nada do mundo antigo é aproveitado, e existe um comando que apaga tudo. Os jogadores foram avisados.
Este changelog conta o que quebrou e por quê, não só o que mudou. Quase tudo aqui nasceu de um defeito medido, e vários dos defeitos foram meus.
1. As cidades saíram do terreno e foram para dentro de uma sala
O problema. A cidade nascia ao lado do altar do chefe, em terreno aberto, e cada etapa dependia do relevo: achar ponto plano, aplainar, esperar o heightmap do frame seguinte, assentar peça por peça. Cada uma dessas etapas produziu defeito medido ao longo das versões — peça flutuando 1,68 m, portal enterrado 1,01 m, 133 peças de cercado onde cabem 51 (anéis sobrepostos de centros levemente diferentes), e a cidade do Norte Profundo que simplesmente não fundava.
A correção. Cada cidade de bioma agora é uma sala de cripta (forestcrypt_entrance_large, 12 × 12 × 20 m), 5000 m acima do altar do chefe dela. Não há ponto plano a procurar, nada a aplainar, nada a assentar depois, e nenhuma das oito pode falhar por causa do relevo.
Por que 5000 m e por que funciona. Não é invenção: Location.Awake do próprio jogo instancia o interior de qualquer masmorra em transform.position.y + 5000f. E a geometria chega nos outros jogadores sem o mod replicar nada — o DungeonGenerator guarda a lista de salas (hash, posição, rotação) no próprio ZDO (ZDOVars.s_roomData), e o Awake de cada cliente reconstrói localmente em SpawnMode.Client. É o mesmo caminho de toda cripta do jogo.
Uma coisa que eu descartei por medida, não por preferência. O pedido original citava a caverna de troll como modelo. Medindo o banco de salas: a caverna de troll não é masmorra procedural — é location de malha fixa (TrollCave, TrollCave02), sem gerador e sem salas, então não dá para compor nada com ela. A sala de entrada da Cripta era a segunda ideia do pedido e é melhor de todo jeito: chão plano (o de caverna é rocha irregular) e zero spawner de criatura.
2. O traçado das vilas deixou de ser código
O problema. Traçado escrito em C# só se conhece depois de compilar, entrar no jogo e olhar. Cada rodada dessas custava minutos, e vários defeitos de geometria vieram de medida suposta ("a estaca tem 2 m", "a sala é uma caixa vazia").
A correção. Dois moldes, construídos no jogo com martelo e capturados por comando:
mmo_molde salvar spawn— a vila das pedras iniciais.mmo_molde salvar cidade— o traçado que toda cidade de bioma usa ao nascer.
A referência é onde você está e para onde olha, em coordenada local. O molde da cidade só pode ser marcado dentro da sala (mmo_molde cidade ir leva até uma), porque marcado em campo aberto o desenho entraria na parede — e o erro só apareceria na primeira cidade fundada.
Marcas. Uma placa escrita Mercador, Emissário, Arauto ou Leilão não é copiada como placa: ela marca onde vai o NPC ou o baú. Portal de madeira marca o portal da cidade; portal de pedra, o do chefe. Assim o mesmo desenho vira oito cidades diferentes — só os NPCs e o destino do portal mudam.
O que o molde deliberadamente não copia: o poste que segura a placa (andaime, não desenho — medido, os quatro pilares estavam todos a 0,55 m da sua placa) e as peças que o próprio mod colocou.
3. A vila inicial voltou para o ponto de renascimento
O tier 1 deixou de ser uma cidade gerada. O respawn é a vila: o mod marca o tier 1 como fundado nas pedras iniciais e não coloca nada por conta própria — a vila vem do molde spawn. O mercador avulso que o mod punha ao lado do templo saiu, porque quem atende ali agora é o mercador da vila.
4. Portais: três por cidade, liberados pelo gatilho do altar
- No spawn, um portal por cidade. Ele só nasce quando a cidade é fundada — ou seja, quando alguém chega ao altar daquele chefe. É o mapa se abrindo aos poucos em vez de já vir com sete destinos prontos.
- Ao lado do altar, um segundo portal, para quem derruba o chefe entrar na cidade dali mesmo.
- Dentro da cidade, os pares dos dois.
A etiqueta do par do chefe é própria (mmo_boss_<tier>), e não a mesma do respawn: com três portais na mesma etiqueta o jogo escolheria um deles ao acaso.
5. Economia: a curva saiu, a tabela entrou
O problema, medido. A regra anterior era "valor base × (tier × 10)", com "tier × 100" para quem não tem valor base. Só que 884 dos 893 itens do catálogo têm valor base zero — a regra virava um preço único por bioma: os 67 itens do tier 4 custavam todos exatamente 400, os 95 do tier 5 todos 500, os 163 do tier 8 todos 800. Uma espada e uma pedra pelo mesmo preço. E os nove itens com valor base explodiam: o Draumyx saía a 12.250 enquanto o mercador o comprava por 175.
Uma curva intermediária, e por que ela também saiu. Cheguei a implementar uma curva que ancorava a escala no bioma e usava a cadeia de produção real do jogo para ordenar os itens dentro do grupo. Ela media bem, mas respondia a pergunta errada: o dono do projeto não quer preço derivado, quer preço escolhido.
O que ficou. Uma tabela de 232 itens, prefab=tier:ouro, escrita à mão e validada item a item contra o catálogo do jogo. A tabela é o catálogo: item que não está nela não é vendido por ninguém. Isso aposentou de uma vez o filtro de bioma, o de item fabricado, o de minério bruto e o de variante interna FW_/SP_.
Adagas Cruzadas não estão na tabela: elas herdam tier e preço da faca base pelo nome do prefab (AdagasCruzadas_KnifeSilver_Dual → KnifeSilver). Assim acompanham o preço da faca quando ele mudar, e uma adaga nova entra na loja sozinha.
Outras mudanças de economia:
- Inflação removida. Ela multiplicava todo preço por
(1 + p)^(dia/período)— incompatível com uma tabela escolhida à mão: o número escrito é o número que se paga. - O mercador compra tudo a 20% do valor. Saíram as duas exceções que existiam: joia pagava o valor cheio (era a melhor fonte de ouro do jogo) e item do bioma do próprio mercador pagava fração diferente.
- Melhoria acumulativa a 30%: 30% do valor para ir de 1 → 2, 60% de 2 → 3, 90% de 3 → 4.
- Poções com preço escolhido: 300 / 600 / 800 (vida), 500 (vigor), 500 (eitr). Houve uma regra "50 × o percentual restaurado" e ela foi substituída por estes números — a regra dava 1500 para a menor, porque uma poção que cura 30% não vale 30% de uma que cura 100%: a curva de utilidade não é reta.
- Cajados do Iniciante: 500 fixo, cada um.
6. Drops
- Criatura comum e bot: 60% de chance de largar moedas equivalentes a 5% da vida base, e 25% de chance por peça de dropar o equipamento que estava usando (era 100%, e por isso todo mob abria o inventário inteiro ao morrer — eram cinco configs separadas, todas em 1.0; agora é uma só).
- Chefe de dungeon e chefe mundial: sempre, 10% da vida base em ouro.
"Vida base" é o m_health do prefab, não GetMaxHealth(): o segundo já inclui estrelas e nível de mundo, e um lobo 2-estrelas passaria a valer quatro vezes mais ouro que o mesmo lobo sem estrela.
Defeitos corrigidos nesta versão
Vários destes eu mesmo introduzi durante o trabalho. Ficam registrados porque o modo como falharam é mais útil que o conserto.
As missões não funcionavam — 44.376 exceções por frame. Ao remover a inflação eu apaguei um intervalo de linhas por número, e o intervalo levou junto os cfg.Bind de quatro entradas de missão. O campo continuou declarado e ficou nulo, e QuestTracker.Update lia QuestTrackerEnabled.Value todo frame. Config declarada e não vinculada não avisa nada: compila, carrega, e só quebra em execução. Agora confiro as duas listas (declaradas × vinculadas) antes de fechar qualquer versão.
Os moradores nasciam e sumiam. O log mostrava os três criados na cota certa da sala, e um instante depois eles estavam no chão do spawn, 5000 m abaixo, com 16 cópias acumuladas. Causa: Trader.Start do próprio jogo chama GetComponent<SnapToGround>().Snap(), que usa ZoneSystem.GetGroundHeight — um raio de y=6000 para baixo contra a máscara de terreno. Destruir o componente depois de instanciar não resolve, porque Destroy de componente só vale no fim do frame e o Start corre antes. Agora um prefixo em SnapToGround.Snap cala a função acima de 2000 m.
A mesma classe de erro, mais três vezes. O raio de terreno respondendo "37" para quem está a 5037 m também: acusava de estar na superfície quem salvava o molde dentro da sala (e gravaria as alturas 5000 m erradas); jogava os guardas da cidade no chão do bioma; e o patch antigo de "encostar o NPC no chão" teleportava os moradores para fora. Todos passaram a medir o piso da sala por raio próprio.
Não dava para construir. Dois bloqueios diferentes, e nenhum era meu. O primeiro: a zona de não-construção das pedras iniciais (Location.m_noBuild) — e Location.IsInside mede a distância com Utils.DistanceXZ, ignorando o Y, então o bloqueio do templo sobe verticalmente e alcançava também a sala 5000 m acima. O segundo: !m_allowedInDungeons && InInterior(), e Character.InInterior é literalmente y > 3000. O primeiro tem um prefixo estreito (só libera quando o único impedimento é o templo inicial); o segundo usa a chave global DungeonBuild, que é o interruptor do próprio jogo.
O guardião de dungeon pedia chefe para a oficina. Doze vezes seguidas, com "nenhum ponto de chão válido para o chefe". A bandeira que eu tinha posto calava só a geração; o guardião roda depois, para sempre. Agora ele ignora qualquer gerador dentro de um interior do mod.
Um comando destrutivo com alvo implícito. O pedido era remover dois portais do spawn; eu escrevi portais limpar, que apagou todos — os sete elos com as cidades, construídos e nomeados à mão, sem lixeira. O comando foi substituído por portais remover <tier>: um alvo, nomeado. E existe portais criar para reconstruir os que faltam, porque o elo não mora no portal, mora na etiqueta.
Salvar por cima do molde o destruía. Trader não é Piece, então a captura não via os moradores; e o baú de leilões, que é peça, entrava como baú comum. Aplicar e salvar de novo devolvia um molde sem NPC nenhum. Agora a captura lê o objeto de verdade, e a placa continua valendo para marcar um lugar antes de o objeto existir.
O portal do chefe da Névoa nascia onde não dava para alcançar. O deslocamento fixo de 14 m a leste funcionou em seis biomas e falhou onde o terreno ao lado do altar é rocha em degraus. Agora varre oito direções e exige chão no mesmo nível do altar.
Limpeza e migração
mmo_limpar tudo aplicar apaga tudo do mod neste mundo: cidades, moradores, guardas, bots, acampamentos, portais, a Casa de Leilões com os itens dentro e todas as chaves globais mmo_*.
Duas decisões que fazem a varredura ser segura:
- Ela varre
ZDOMan.m_objectsByID, e não a cena — o lixo que incomoda está quase todo em zona não carregada. - Ela só toca no que tem marca do mod no ZDO. Peça de jogador não carrega nenhuma dessas chaves. Há ainda uma trava explícita: personagem de jogador nunca sai, em modo nenhum.
O que ela não toca: construção de jogador, baús e o que está dentro deles, personagens e inventários, e as chaves globais do jogo base — o filtro é o prefixo mmo_, então a progressão de chefe continua valendo.
No servidor dedicado os comandos de limpeza e mmo_city refundar rodam no servidor, por RPC: quem digita é cliente, e do lado dele a lista de ZDOs tem uma fração do mundo. O servidor confere se quem pediu está na adminlist dele — confiar no cliente para dizer se é admin é o mesmo que não ter checagem.
Comandos
mmo_molde salvar <spawn|cidade> [raio]·aplicar spawn·aplicar cidade <tier>·cidade ir·apagar <nome>·vermmo_city conferir·fundar <tier>·refundar [tier]·ir <tier>·portais criar|remover <tier>·moradores [aplicar]mmo_limpar·aplicar [leilao]·tudo aplicar·chaves [aplicar]mmo_dumploja— catálogo de cada mercador com preço e origem
1.0.0 — 2026-09-18
Versão de relançamento. Decisão do dono do projeto: partir do conjunto de sistemas da 0.17.7 (a versão estável) e descartar o que entrou depois. Como a fonte da 0.17.7 não existe mais (o projeto não tem histórico), a 1.0.0 é o código atual podado até aquele conjunto — com as correções de defeito feitas desde então e a suíte de teste in-game.
Itens: a lista fechou
- Só existem seis famílias de item do mod, e todo o resto é prefab do jogo base: poções (vida menor/média/grande, vigor, eitr), Pedra de Retorno, Mapa do Explorador, Cajados do Iniciante e as Adagas Cruzadas.
- Saiu o mecanismo de "item nomeado do boss". O baú do boss de dungeon dava uma peça com título do chefe e multiplicador de atributo gravados no
customData. Agora ele dá a mesma peça, vanilla puro — semcustomData, sem multiplicador. A configNamedBonusMultiplierfoi removida;NamedItemChanceeNamedQualitycontinuam decidindo se cai e com que qualidade. - Saiu o Selo de Proteção e a proteção de construção que ele ativava. Chaves
mmo_prot_*que já existam no mundo ficam sem efeito. - Guarda de item modificado, novo. No carregamento do personagem e na abertura de cada baú, o mod limpa o que sobrou das versões anteriores, com uma diferença que importa: prefab de mod que não existe mais (relíquia de raid, Selo) é removido; peça vanilla que só carrega
customDatado mod fica, e só ocustomDatasai — apagar a espada de alguém porque sobrou uma chave no dado dela seria punir o jogador por uma decisão minha.mmo_limpaitensfaz a varredura na hora, na mochila e em todos os baús carregados.
Mercador
- Só vende item do próprio bioma. O tier do item tem de ser exatamente o tier da cidade.
- Preço novo: valor base do item × (tier × 10); item sem valor base custa tier × 100. A maioria do que é fabricado tem
m_value0 no Valheim, então o segundo caso é o que vale quase sempre — sem ele, quase tudo sairia de graça. - Minério bruto não é mais vendido, só o refinado (a
tier × 100). Regra por nome de prefab, porque o jogo não marca "bruto" em campo nenhum: todo minério bruto termina emOreouScrape nenhuma barra termina assim. - O item duplicado na loja tem causa medida e está corrigido. A loja da Planície mostrava dois "Presa de Draugr" (Perfurante 47 e 70) e a do tier 3 mostrava três "Machado de bronze". Não era o mod duplicando: o
ObjectDBdo jogo tem prefabs com o mesmo nome de exibição do item real —FW_BowDraugrFangeSP_BowDraugrFangao lado deBowDraugrFang. ProcureiFW_AxeBronzeem todo DLL de plugin e em todos os arquivos de dados do jogo: não existe como texto, são prefabs que o próprio jogo cria em memória. Agora o catálogo descarta a variante (prefixo de 2–3 maiúsculas cujo resto é um prefab existente) e deduplica pelo nome que o jogador lê, como rede de segurança. Minha primeira hipótese — de que era o ajuste de arquearia mexendo numa cópia e não na outra — estava errada.
Vilas
- Peça flutuando ou enterrada: causa encontrada. A leitura de altura do chão caía, quando o colisor do terreno não estava carregado, para a altura do terreno virgem — que ignora a terraplanagem. Numa praça nivelada para a cota 30 com o terreno original em 33, a peça nascia 3 m no ar; com o original em 27, nascia enterrada. E era intermitente, porque dependia de o colisor estar carregado naquele instante. Agora existe leitura estrita do terreno, e quem coloca peça no chão recusa-se a chutar a cota: sem chão legível a peça não entra e volta na próxima passada. Valeu para peça de cidade, portal e para a cota do centro da cidade, que é a referência de tudo o mais.
Equipamento
- Sumiram os slots de Arma, Mão esquerda e Arco do painel de equipamento. Eles já tinham saído da lista de slots em 15/09, mas o mapa de posições do painel continuava com os índices da versão de 10 slots: cada peça caía na posição de outra (Flechas no lugar da Arma, Capacete no da Mão esquerda) e as três posições extintas seguiam reservadas, deixando buracos. Agora são duas colunas para os sete slots reais.
Teleporte
- Não existe mais restrição de minério. Portal e Pedra de Retorno levam minério e barra. As configs
PortalCarriesOreeReturnStoneIgnoresOresaíram: uma opção que ninguém vai desligar só aumenta a superfície do que pode estar diferente entre dois servidores.
O que saiu de sistema
- Raids e relíquias (seção
31 - Raids): construtor, kit do OdinsHollow, loot elemental, resgate de relíquia, comandosmmo_raid,mmo_hollow,mmo_relic,mmo_fx. Relíquias (MMO_RaidT*) deixam de existir. As quatro raids fundadas no mundo ficam como peças órfãs a 2600 m. - Arena PvP / Torre Celestial (seção
33 - Arena PvP), com o Arauto da Torre, a janela emmo_arena. Voltam as regras normais de morte em todo lugar. - Dependência do OdinsHollow e a seção
32 - Compatibilidade. O mod não depende mais dele, e ele saiu da instalação local (guardado embackup-OdinsHollow-removido/). Quem mantiver o OdinsHollow instalado perde o menu do martelo — era isso que a seção 32 consertava. - XP e level de clã (já removidos na 0.25.99; permanecem fora).
- Tecla P: não existe atalho por padrão desde a 0.25.x — a config virou
PainelAtalhoe nasce vazia, justamente para oOpenPanelKey = Pgravado nas instalações antigas não continuar valendo.
Itens 15 e 16
- A guarnicao do acampamento nasce junta e espalhada, na fundacao e na reposicao depois do saque. Antes a reposicao vinha 2 bots a cada 15 s, o que enchia o acampamento aos pingos durante minutos. As posicoes saem de um circulo dividido pela cota, nao ao acaso: com angulo aleatorio, doze bots se agrupam de um lado e deixam metade do acampamento vazia.
- Teto de 12 por acampamento no codigo (
MaxBotsPerCamp), acima da cota por tier (4 a 12). Reposicao so depois de o bau de guerra ser saqueado e o tempo de saque vencer. - Eventos de raid de boss conferidos de verdade. O caso de teste nao aceita mais "estar na lista" como resposta: o jogo so sorteia evento com
m_enabled, semm_devDisablede comm_random, e um clone que perdesse qualquer um dos tres ficaria na lista para sempre sem nunca acontecer. Medido: 6 de 6 presentes e 6 de 6 sorteaveis, de 37 eventos no total, sem chave de espera presa.
Verificação
Suite in-game na 1.0.0. Verde: mod/patches aplicados, eventos/eventos de raid de boss injetados (6 de 6 sorteaveis), bots/limpar remove todos os bots, bots/teto de 12 bots por acampamento, mercador/catalogo sem nome duplicado (nos oito tiers), descobre/pecas de grade e cerca disponiveis, descobre/pivo acima da base das pecas que afundam, tour/funda e mede as oito cidades, vila/anel de todos os oito tiers, vila/vila inicial e simetrica e vila/medida de todas as cidades.
A medida que prova as vilas: o anel de cada cidade fundada foi medido peca por peca -- 7 de 8 cidades fundadas, e em todas as sete 0 pecas flutuando, 0 enterradas e 0 tortas (35 a 95 pecas por anel). A variancia do terreno ficou entre 1 e 2 m em 81 amostras por cidade: plano na pratica, nao matematicamente plano.
Fica aberto, e eu nao fecho sem medir: a cidade do tier 8 (Abrigo de Morkhalla, Norte Profundo) nao funda -- e o unico tier que falta, e o caso e parecido com o da cidade do pantano de 0.25.98. E o caso vila/medida da vila inicial falha por um defeito do proprio teste, agora medido: o teleporte para o centro da vila nao completa em 150 s, e sem chegar la nenhuma peca instancia para ser medida.
Versão de relançamento. Decisão do dono do projeto: partir do conjunto de sistemas da 0.17.7 (a versão estável) e descartar o que entrou depois. Como a fonte da 0.17.7 não existe mais (o projeto não tem histórico), a 1.0.0 é o código atual podado até aquele conjunto — com as correções de defeito feitas desde então e a suíte de teste in-game.
O que saiu:
- Raids e relíquias (seção
31 - Raids): sistema inteiro removido — construtor, kit do OdinsHollow, loot elemental, resgate de relíquia, comandosmmo_raid,mmo_hollow,mmo_relic,mmo_fx. Relíquias (MMO_RaidT*) deixam de existir: item que estiver em inventário ou baú é apagado pelo jogo no primeiro carregamento (prefab desconhecido). As quatro raids fundadas no mundo ficam como peças órfãs a 2600 m, sem sistema por trás. - Arena PvP / Torre Celestial (seção
33 - Arena PvP): removida por inteiro, com o Arauto da Torre, a janela e o comandommo_arena. Voltam as regras normais de morte em todo lugar. - Dependência do OdinsHollow e a seção
32 - Compatibilidade: o mod não depende mais do OdinsHollow. Quem mantiver o OdinsHollow instalado perde o menu do martelo (era isso que a seção 32 consertava) — desinstale-o do perfil. - Selo de Proteção e a proteção de construção que ele ativava. Chaves
mmo_prot_*que já existem no mundo ficam sem efeito. - XP e level de clã (já removidos na 0.25.99; permanecem fora).
O que fica, por decisão explícita: adagas duplas, Cajados do Iniciante (kit inicial), poções de vida, de vigor e de eitr, Mapa do Explorador e Pedra de Retorno. Todo o resto de item é vanilla.
O que fica do trabalho recente, porque é correção e não sistema: teto de 12 bots por acampamento e reposição só após saque; mmo_bot limpar/contar; logs de depuração nascendo desligados (seção 34 - Log de diagnostico); poda das chaves de caçada antigas; consolidação de pilhas após "Pegar tudo" e mmo_juntar; mmo_chaves.
Aviso de compatibilidade: a Thunderstore passa a ter a 1.0.0 como última versão, acima da 0.25.99 — instalação nova pega a 1.0.0.
0.25.99 — 2026-09-17
- Log de depuração de bot estava LIGADO por padrão e enchia o log de todo jogador.
28 - Faccoes / DebugLognasciatruedesde que foi criado — a própria descrição dizia "desligue quando estiver estável", e nunca foi desligado. Medido no log de uma sessão real: 220 linhas, uma por boneco a cada 5 s, cada uma uma string longa com estado de animador, ossos e camadas. Escrever log é síncrono, então isso custava em toda partida. Agora nascefalse. A descrição também estava errada: dizia 20 s e o código usa 5 s. - Seção nova
34 - Log de diagnostico, desligada, e três instrumentações foram para trás dela. O padrão que se repetiu neste projeto é instrumentação de investigação ficar em produção depois que o defeito foi resolvido. Agora passam pela chave: o rastro de pilha de cada chefe de dungeon destruído (BossWatchmonta umStackTracecom reflexão em cada destruição) e a lista de peças de cada portão de raid (~1500 caracteres por portão, 14 portões por raid, a cada carregamento). O resumo de cada portão continua sempre no log; só a lista de peças saiu. 31 - Raids / DebugSolonasciatrue. Um servidor recém-instalado deixava qualquer um entrar sozinho numa raid feita para grupo. Agora nascefalse.9 - Drop de equipamento / LogSkippedInternalItemspassou a nascer desligada. É ajuda de desenvolvimento (preencher o mapa de remap de arma interna de criatura), não informação para quem joga, e nascia ligada.- Acampamento de facção parou de repor bots para sempre. A reposição era contínua: 2 bots a cada 15 s sempre que a contagem estivesse abaixo da cota, por qualquer motivo. Bot morto por lobo, por bot rival ou por jogador de passagem reabria a vaga, e quem ficasse perto de um acampamento via bot nascendo sem parar. Agora a guarnição é completada uma vez por ciclo de saque (chave
mmo_camp_pop_f_t): o acampamento enche ao ser fundado e só volta a repor depois de ser saqueado, quando o tempo de saque passa — aí reseta para a cota cheia. - Teto duro de 12 bots por acampamento.
WantForlimitava a 20 e a config pedia até 12; agora o teto está no código (MaxBotsPerCamp = 12), porque a config viaja do servidor para o cliente e um valor alto ali multiplicaria bots em 16 acampamentos. - Pilhas parciais do mesmo item passam a se juntar depois de "Pegar tudo". A causa é do próprio jogo:
Inventory.MoveAllinsere cada item na coordenada que ele ocupava no baú; se aquele slot está livre na sua mochila, nasce pilha nova em vez de somar na que você já tinha, e o jogo nunca volta atrás. Cada saque deixava mais pilhas parciais — medido numa mochila real: moeda 467/999 e 99/999, pedra 14/50 e 2/50, madeira em três pilhas. Agora a mochila é consolidada depois do "Pegar tudo", e existemmo_juntarpara fazer na hora. A barra rápida e os slots de equipamento ficam intactos. - O engasgo "quando alguém mata um mob" tem causa medida, e ela foi cortada na raiz. No log do cliente, a morte de um troll é seguida em ~1 s por três
client got keys 428— o servidor reenviou a lista inteira de 428 chaves globais para todo mundo, três vezes. O mecanismo, confirmado no código do jogo:RPC_SetGlobalKeycompara a string completa "chave valor"; se ela mudou, chamaSendGlobalKeyspara todos os peers; e cada cliente, ao receber, fazClearGlobalKeyse rodaGlobalKeyAdd+Game.UpdateWorldRatespara cada uma das 428 chaves. Quem gravava a chave a cada abate era o XP de clã: cada membro perto do abate fazia o cliente dele mandarxp, e o servidor regravavammo_clan_<id>com o valor novo. Três membros perto = três reenvios. - XP e level de clã foram removidos. Decisão do dono do projeto: só consumiam memória e processamento. Some o RPC por abate de cada cliente de clã (a raiz do problema, não só a chave), some o bônus de +1%/level em XP e ouro de missão, somem as quatro configs (
XpShare,XpPerLevel,MaxLevel,BonusPerLevel). O painel do clã mostra nome, tag, dia de fundação, membros e cofre. A chavemmo_clan_<id>mantém o campo de XP no formato para não quebrar leitura — ele só nunca mais muda. Clientes antigos ainda mandam a opxp; o servidor lê e ignora. - 105 das 428 chaves eram lixo de caçada que nunca era apagado.
mmo_hunt_up_<tier>_<semana>emmo_hunt_done_<tier>_<semana>acumulavam para sempre — e cada chave morta encarece todo reenvio, para sempre. Agora as semanas anteriores à passada são apagadas, 2 por tick de 30 s (cada remoção também dispara um reenvio completo; apagar 100 de uma vez seria o próprio engasgo). Uma centena sai em ~25 min, uma vez. As 277 chaves de raid ficam para uma revisão própria. mmo_chavesconta as chaves globais do mundo por família. É o instrumento para ver esse número cair e para pegar a próxima família que crescer sem parar.- O selo de nível de mundo é respeitado, não burlado — e agora é medido. O jogo só empilha itens com o mesmo
m_worldLevel(FindFreeStackItemeIsSameTypecomparam nome + qualidade + nível), e esse selo entra no item quando ele nasce. Item pego antes e depois de o nível do mundo mudar nunca se junta. Ignorar o selo faria flecha e equipamento de forças diferentes virarem uma pilha só, porque o nível de mundo entra no dano e na armadura. Então o selo continua valendo e apenas é contado: quando sobra pilha sem juntar por causa dele, aparece "N pilha(s) nao juntam: nivel de mundo diferente". É o que vai dizer se a causa restante é essa, em vez de eu adivinhar.
0.25.98 — 2026-09-16
- Arena PvP, segunda fase: a luta existe. Fila por bloco de 10 andares, pareamento automático, contagem, combate, vitória por morte do oponente, progressão de andar pelas regras da referência (abaixo do 200 uma vitória sobe um bloco e uma derrota desce; do 200 em diante são 10 vitórias para subir e 4 derrotas custam a vaga), prêmio em moedas, classificação dos que subiram mais alto e o Arauto da Torre na cidade hub, com janela própria. Continua desligada em
33 - Arena PvP / Enabled. - Como o jogo separa lutador de plateia. O servidor cria a luta e diz quem são os dois; cada lutador, no próprio cliente, escreve o id da luta no ZDO do seu personagem. O ZDO é replicado, então qualquer cliente responde "este é um dos dois?" lendo um inteiro — sem RPC, sem tabela local, e igual no cliente de quem assiste. As duas regras ficam simétricas e baratas, o que importa porque elas rodam a cada golpe: lutador é jogador dentro da caixa com o id da luta corrente; espectador é jogador dentro da caixa que não é lutador, e ele não causa nem recebe dano.
peer.m_playerIDé sempre 0 nesta build, e isso deixaria a arena morta para todo cliente remoto. O único lugar que escreve esse campo éZNet.RPC_PlayerID, registrado emRPC_PeerInfo— e nada no jogo chama esse RPC:"PlayerID"aparece uma vez na assembly, no próprioRegister. Toda operação de cliente remoto era resolvida como jogador 0 e descartada em silêncio: o botão de entrar na fila não fazia nada, sem mensagem e sem log. O id persistente passa a viajar no payload, como o clã já faz, e o servidor guarda uma tabelauid -> rotamontada na entrada da fila.- A rota é o remetente do RPC, e não
peer.m_characterID. OcharacterIDviraZDOID.Noneenquanto o jogador está morto —Game._RequestRespawnchamaZNet.SetCharacterID(ZDOID.None), que avisa o servidor. Ou seja, ele desaparecia exatamente no instante em que o resultado e o prêmio são enviados: a vitória era gravada na ficha e a moeda nunca chegava. - O uid vir do payload abriria a porta para um cliente se dizer outro, então o vínculo
uid -> rotarecusa operação de uma rota diferente quando aquele uid já está ligado a uma rota viva, e recusa qualquer um que se diga o host. - Lutador que sai da sala deixa de ser lutador. A definição exigia "dentro da caixa" mas o teste de posição não estava lá, e o patch que força PvP se apoiava nele: quem atravessava o portal no meio da luta ficava com PvP ligado no mundo aberto por até cinco minutos, já fora da isenção de morte — qualquer um podia matá-lo com perda cheia de XP, karma e item.
- Um dentro e um fora: o golpe não passa. Faltava a garantia mais óbvia da arena — alguém de fora, com arco, acertava um lutador no ringue e decidia a luta, ou matava um espectador que não podia revidar.
- Nocaute duplo anula. A varredura devolvia o primeiro da lista, então o lutador A sempre perdia quando os dois morriam no mesmo tique — um golpe que mata e leva junto decidia a luta pela ordem do vetor.
- Lutador que reconecta no meio da própria luta volta para o ringue. O cartão de lutador vive no ZDO do personagem, e o personagem é recriado no login: ele voltava sem marca, ou seja, espectador da própria luta, e com a rota trocada seria dado como ausente e perderia por desistência.
- O perdedor também entra na classificação. Só o vencedor era reescrito, então quem só perdeu aparecia com o andar antigo para sempre.
- O prêmio é do andar da luta, não do andar de quem venceu — o par se forma por bloco, então os dois podem estar em andares diferentes dentro dele, e a mesma luta valia valores diferentes dependendo de quem ganhasse. E entra teto antes do arredondamento: com passo 3 em bloco alto a potência passava de 1e9 e
Mathf.RoundToIntdevolvia lixo, podendo sair negativo — prêmio que tira moeda de quem venceu. - A recuperação de luta pendurada mudou de gancho. Ela rodava num postfix de
Game.Start, mas as chaves globais são carregadas porZNet.Start, e os dois sãoStartdeMonoBehaviour— a ordem entre eles não é garantida pela Unity. Agora roda no primeiro tique do servidor, onde o mundo está provadamente carregado, e tira da fila só os dois lutadores em vez de esvaziá-la. mmo_arena rebuilddesmancha a luta antes de apagar a sala. Sem isso os dois ficavam a 3200 m sem chão, marcados como lutadores mas com a caixa apagada — sem isenção de morte e com PvP forçado, até o tempo limite estourar.- O lutador chega curado. A regra combinada é "o equipamento é o que você tem, o andar equaliza", mas entrar com 12 de vida porque um lobo mordeu no caminho não equaliza nada quando a vitória é por morte.
- Três travas contra nascer dois Arautos. A marca é uma chave global, e escrever passa pelo servidor e volta em broadcast: entre dois clientes que rodam o mesmo tique na mesma janela ela ainda é falsa para os dois. Agora só tenta quem está na cidade, quem acabou de instanciar espera a marca, e um arauto duplicado é removido.
- A caixa da arena era expandida duas vezes — 6 m na construção e 6 m no predicado — e a arena valia 12 m para fora de cada parede.
- A caixa e a luta passam a ter cache de uma entrada: as duas são lidas a cada golpe e faziam
SpliteParsede string toda vez. - O botão ASSISTIR exige estar na base da torre, como o
mmo_arena in/galjá exigia: teleporte de qualquer ponto do mapa para dentro da caixa transformava a isenção da plateia em fuga da morte. mmo_arena morreravisa quando não há luta e oDebugSoloestá desligado — nesse caso a morte cobra normalmente, de propósito, e o teste parecia falhar por defeito.- A luta solo do
DebugSolopassa a registrar derrota: dar vitória a quem acabou de morrer validava o caminho errado e pagava prêmio por morrer, repetível à vontade. - O servidor parou de decidir com dado que o cliente pode escrever.
ZoneSystem.RPC_SetGlobalKeynão confere o remetente — conferido na assembly — então qualquer cliente escreve qualquer chave global. Enquanto o servidor lia a luta da chave, um cliente forjava um registro de luta contra um oponente inexistente, o tique dava a vitória por desistência e pagava o prêmio do andar 199 a cada segundo — e podia nomear qualquer jogador real como perdedor, derrubando o andar dele sem que ele soubesse. A luta, a fila, o contador de id e as fichas passam a viver na memória do servidor; as chaves continuam sendo onde tudo persiste, mas viraram espelho de leitura. O único momento em que o servidor lê a chave é a recuperação de boot, e lá ele só anula. - A identidade passou a ser provada, e não declarada. O uid persistente está no ZDO do personagem, escrito pelo próprio jogo (
PlayerProfile.LoadPlayerData→Player.SetPlayerID→zdo.Set(s_playerID, id)), e o servidor tem todos os ZDOs. Sem a conferência, mandar o uid de outro jogador logo depois de um restart prendia a rota dele na minha: a vítima ficava trancada fora da arena, eu lutava com o nome dela, o ouro caía no meu bolso e a derrota ia para a ficha dela. Com a prova, a rota nova também pode assumir o uid mesmo com a antiga ainda viva — que é o que faz a reconexão funcionar, porque o peer de quem caiu a conexão só é recolhido depois do timeout de 30 s doZRpc. - Uma caixa forjada desligava o jogo inteiro. Toda regra da arena sai de
Dentro(), que sai demmo_arena_box. Uma única escrita de uma caixa de 100 km fazia a arena valer para o mapa todo: ninguém mais derrubava item ao morrer, nenhum golpe entre jogadores acertava, ninguém construía nada — e a chave persiste no arquivo do mundo, então sobreviveria ao restart. A caixa agora é recusada quando o tamanho ou a altura não cabem na descrição da sala. - A proibição de poção e comida era código morto. O patch era em
Humanoid.ConsumeItem, masPlayersobrescreve o método — conferido na assembly: o corpo da base só chamaCanConsumeIteme devolvefalse; quem aplica o efeito, come e remove o item é o override. A chamada despacha virtualmente, então o prefixo nunca rodava: poção, comida e mead de cura funcionavam normalmente na arena, ao contrário do que a própria janela prometia ao jogador. - O aviso de morte de cliente remoto era jogado fora em silêncio. O patch de morte mandava a string crua
"morri", e o servidor descarta payload com menos de três campos. A luta só terminava quando o servidor notasse a vida no ZDO — uma janela de uns 10 s, até o respawn destruir o personagem — ou no tempo limite de 5 minutos, anulada, sem prêmio para quem venceu. - A isenção de itens exigia posição, e o resto da isenção exigia evento. Quem estava queimando no mundo pegava o portal da torre, morria sem luta nenhuma e ficava com o inventário inteiro — justamente a metade da isenção que o projeto queria negar, já que as outras cinco penalidades usavam
SemPenalidadee cobravam. As duas agora usam a mesma regra. - O retorno ao ringue era uma cura cheia sob demanda. Ele foi escrito para quem reconecta no meio da própria luta, mas não checava nada: um cliente modificado pedindo o retorno a cada vez que ia morrer simplesmente não perdia, e cada vitória pagava prêmio e andar. Agora é uma vez por luta, só para quem não está no ringue (conferido pela marca do ZDO e pela posição), e sem cura — a cura acontece só na entrada, e é o servidor que decide isso, não o cliente.
mmo_arena rebuilddeixava os dois lutadores caindo de 3200 m. Anular a luta não bastava: a sala é apagada e a chave da caixa também, então eles ficavam sem chão e já fora de toda regra da arena — a queda cobrava penalidade completa, itens no chão do ponto de impacto. Agora eles são retirados para a base da torre antes, e com luta em andamento o rebuild é só do dono do servidor: oServerAdminsfica na seção "18 - Rede", que oConfigSyncnão sincroniza, ou seja num cliente esse valor é o arquivo dele.- A liberação de "zona pronta" valia para todas as zonas. O patch ignorava o
zoneIDe decidia pela posição do jogador, então enquanto ele estivesse na arena a checagem ficava desligada em toda zona que aquele cliente simulava — inclusive as do mundo que ainda estavam carregando, que é exatamente o que a checagem existe para proteger. - Furo que fica, declarado: a memória do servidor é semeada pelas chaves no primeiro acesso depois do boot, então uma ficha forjada antes de um reinício ainda seria adotada. Fechar isso de verdade exige guardar estado fora das chaves globais, que é um projeto próprio — é o mesmo furo que a moeda de clã já tem hoje.
- Teste automatizado dentro do jogo.
mmo_testroda uma suíte de casos no mundo, etools\mmo-test.ps1faz o ciclo inteiro sem clique: compila, sobe o Valheim sem janela (-batchmode -nographics -mmotest), entra sozinho num personagem e mundo isolados (MMOTEST, nunca o seu save), roda a suíte, gravaBepInEx/mmo-test-result.txte fecha o jogo com código de saída. Os casos são corrotinas porque quase tudo na arena precisa de frames para assentar — teleporte gera zona, o tique do servidor é de 1 s, ZDO replica no envio seguinte; um teste síncrono mediria o estado antes de ele existir. - A suíte achou quatro defeitos na primeira hora, e três deles nenhum revisor pegaria.
- O patch do Arauto da Torre nunca foi aplicado. O parâmetro se chama
charactere eu havia escritohuman, então o Harmony recusou a classe inteira:Parameter "human" not found in method virtual bool Trader::Interact(Humanoid character, bool hold, bool alt). Falar com o NPC não abria nada. Um patch recusado não quebra nada visivelmente — a funcionalidade simplesmente não existe, em silêncio. Agora a lista de classes recusadas fica guardada e o primeiro caso da suíte afirma que ela está vazia. - A contagem da luta media o tempo num relógio congelado.
ZNet.GetTimeSeconds()só avança no servidor quandoGetNrOfPlayers() > 0(conferido emZNet.UpdateNetTime), e num host jogando sozinho essa contagem é zero — exatamente o caminho de teste. A luta ficava parada em "preparando" para sempre. O estado vivo passa a ser medido pelo tempo real acumulado no próprio tique; o tempo de rede continua gravado, mas só como registro. - O teleporte para o ringue podia ser recusado, e o código não olhava o retorno.
Player.TeleportTodevolvefalsese já há teleporte em curso ou se faz menos de 2 s desde o último. Quem entrasse pelo portal da torre e fosse pareado no mesmo instante ficava marcado como lutador sem nunca chegar ao ringue — e, como lutador agora exige estar dentro da caixa, a luta morria no tempo limite sem explicação. Agora o cliente insiste até o jogo aceitar, por até 30 s. - A isenção de morte foi finalmente verificada em jogo, e não por leitura de código: o caso mede level, XP, karma e número de itens antes e depois de morrer dentro da arena, e a linha
caiu na arena; itens ficam com eleaparece no log. O caso irmão prova o outro lado — sem luta em andamento, morrer lá dentro cobra normalmente, porque a arena não pode ser esconderijo. - Três casos provam que chave global forjada não vale nada: caixa gigante recusada pelo saneamento, ficha de andar 199 ignorada, e registro de luta forjado que não paga prêmio nem muda andar. Esses casos não precisam de segundo jogador — eu escrevo a chave como um cliente adulterado escreveria e afirmo que nada aconteceu.
- O espelho da luta passa a ser reescrito a cada segundo pelo servidor:
RPC_RemoveGlobalKeytambém não confere remetente, então qualquer cliente apagavammo_arena_boutcom um RPC e, para todos os clientes remotos, a luta deixava de existir — o PvP forçado caía, o dano entre os dois parava e morrer na arena voltava a cobrar tudo. Reescrever é de graça: o jogo ignora chave com string idêntica. - Resultado e prêmio que chegam enquanto o jogador está morto não são mais perdidos. A luta é decidida pela morte, e o jogo destrói o
Playerna morte e só o devolve no respawn 10 s depois — a mensagem com a moeda chegava exatamente nessa janela e era descartada em silêncio. Agora fica guardada e é entregue no renascimento. - O host deixou de ser dado como ausente nos 10 s em que está morto (a inscrição na fila era removida sem aviso, e o prêmio de uma luta decidida nessa janela sumia).
mmo_arena rebuildpassa a ser só do dono do servidor. Num cliente ele apagava as chaves sem tocar na memória do servidor, que seguia com a luta rolando, e a retirada dos lutadores era descartada por falta de rota. E "admin" nunca foi trava ali:ServerAdminsfica na seção "18 - Rede", que oConfigSyncnão sincroniza — num cliente aquele valor é o arquivo dele.- Os pontos de chegada da arena passaram pelo mesmo saneamento que a caixa já tinha: eles viram destino de teleporte decidido pelo servidor, e escrever
mmo_arena_a -5000,500,-5000arrancava o próximo lutador pareado para o meio do nada, com o cartão de lutador na mão e fora da isenção. - As fichas passam a ser carregadas todas de uma vez no boot do servidor. Semear no primeiro acesso de cada jogador deixava a brecha de forja aberta pelo tempo que o atacante quisesse — bastava escrever a chave e não tocar na arena antes.
- Teste de DOIS CLIENTES de verdade, automatizado.
tools\mmo-test-duo.ps1sobe duas instâncias do Valheim sem janela, com personagens separados (MMOTESTeMMOTEST2), e elas conversam por TCP em 127.0.0.1 — sem Steam P2P e sem segunda conta. Isso fecha o que era impossível provar sozinho: identidade do remetente, roteamento servidor→cliente, aviso de morte partindo de cliente remoto, teleporte dos dois lados, e a recusa de um cliente que se diz outro jogador. ZNet.UpdateClientConnectorestá definido e nunca é chamado nesta build. Ele é quem termina a conexão por socket bruto; não aparece noUpdatenem noFixedUpdate(que só chamaUpdateNetTime). Ou seja, entrar por IP direto é código morto no cliente de varejo: oZConnector2nasce e ninguém o consulta, então a conexão fica emConnectingpara sempre — sem nem falhar. O modo de teste gira essa manivela; nenhum jogador passa por esse caminho.- Também faltava o lado servidor:
ZNet.OpenServer()só monta socket para Steamworks e PlayFab, não existe ramo paraCustomSocket. ComoCheckForIncommingServerConnections()só precisa dem_hostSocketpreenchido, o modo de teste monta oZSocket2e o atribui. EZSocket2.StartHost()usaBindSocket(s_dataPort, s_dataPort+10)coms_dataPortestático valendo 0 num processo cliente — o bind funcionava numa porta efêmera mas gravavam_listenPort = 0, então a porta publicada era 0 e ninguém tinha para onde conectar. - Cada processo passou a ter log próprio. As duas instâncias saem da mesma instalação e disputam o
BepInEx/LogOutput.log— medido: só uma consegue escrever, e sem erro visível. Sem isso, quando o segundo processo morria calado não havia absolutamente nada para ler. Agora há log Unity por processo (-logFile) e um rastro do mod que relata a cada 10 s o que ainda falta (ZNet, status da conexão, cena, zonas, jogador). - Um passe falso foi encontrado e fechado. O caso que prova a recusa de identidade forjada afirma que "nada aconteceu" — e ele passava mesmo quando a tentativa nunca era feita, porque a coordenação entre os dois processos era uma janela de tempo fixa e a visita começava a suíte um segundo depois de ela fechar. Agora é aperto de mão: a visita confirma que mandou, o anfitrião afirma que a confirmação chegou, e só então a ausência de efeito significa alguma coisa.
- A luta 1 contra 1 está verificada em jogo, não por leitura de código: fila com dois jogadores distintos, par formado no andar 21, contagem, combate, decisão por nocaute com a morte partindo do cliente remoto, andar 21→31 para o vencedor e 21→11 para o perdedor, prêmio de 105 moedas pago e os dois na classificação.
- Cinco casos de regressão passam a cobrir consertos que até aqui valiam só por leitura de código: o espelho da luta reposto depois de um cliente apagá-lo, ponto de chegada forjado recusado, descarte da luta limpando a fila na memória (a prova real é conseguir entrar na fila de novo), contador de id não estourando no teto do int, e a vaga na fila sobrevivendo aos 10 s em que o jogador fica morto. Suíte de um cliente: 15 casos.
- Reforma simétrica das vilas, começando pela inicial — e feita com medida, não com palpite. O construtor do cercado dividia o perímetro por
2, um número mágico que supunha peças de 2 m de largura. Nenhum prefab de cerca tem 2 m: medida no jogo, awood_fencetem 2,69 m. Com raio 15 isso pedia 47 peças onde cabem 35, cada uma encavalada 0,64 m na vizinha. E o erro valia para todos os tiers, que usam prefabs diferentes — estaca, muro de pedra, estaca dvergr, meia parede de freixo — todos tratados como 2 m. - O construtor passa a medir o prefab (uma vez, sem rotação e sem rede, pelo mesmo
m_forceDisableInitque o boneco de facção já usava) e a arredondar a contagem para múltiplo de 4, que é o que dá ao anel dois eixos de simetria e faz o portão cair no ângulo exato pedido. Vila inicial: 47 peças → 36, passo de 10°, e o portão a 90° exatos em vez de 92,06°. - Medir a peça já colocada não serve, e essa foi a primeira régua que eu escrevi errado:
Collider.boundsé uma caixa alinhada aos eixos do mundo, e as peças do anel estão giradas — a caixa de uma cerca girada mede a diagonal da projeção, não a largura. A primeira leitura me deu 2,67 m e uma "sobreposição" que podia ser artefato; a peça sem rotação deu 2,69 m e confirmou o defeito. - Os quatro moradores num raio só. Estavam em dois círculos — mercador e emissário a 3,5 m, arautos a 4,5 m. Agora os quatro a 4,5 m nas quatro direções cardeais. O leste continua sendo o mercador de propósito: quem entra pelo portão dá de cara com quem vende.
- E a reforma alcança mundos que já existem. Mudar o lugar de nascimento só valeria para cidade fundada dali em diante — ou seja, nunca chegaria ao servidor, onde a vila já está fundada. O teste acusou exatamente isso. Entra uma passada de realinhamento: o cliente que está perto da cidade confere cada morador contra a direção do papel dele e move só quem está fora do lugar, sem recriar nada — o NPC é o mesmo, inventário e estado intactos.
- Os 7 portais já eram simétricos em torno do sul (105° a 255°, passo de 24,94°) e não foram tocados. O baú do leilão fica sozinho numa diagonal limpa a 135°: duplicá-lo só para equilibrar seria enfeite, não simetria.
- A suíte ganhou linhas de medida no relatório (largura real de cada peça, vão entre elas, raio e ângulo de cada coisa em volta do centro) e dois casos de vila: um que mede e um que afirma a simetria — total divisível por 4, portão no eixo com tolerância de meio grau, peças encostando sem furo, e todos os moradores no mesmo raio.
- Arena maior, com arquibancada cercada e portal em patamar próprio. A sala passou de
OH_Ruins_Room_2(17×32) paraOH_Ruins_Hall_3(36×34) e o ringue de 11 m para 24 m. A arquibancada virou três degraus (era dois) porque a sala dobrou de largura — com o desenho antigo sobrariam 9 m de chão vazio de cada lado. Na beirada do primeiro degrau entram 32 grades de ferro (iron_grate, 2,01 × 3 × 0,2 m, medida no jogo): a plateia não entra no ringue, e a grade é vazada de propósito — parede cega tiraria o único motivo de existir uma arquibancada. - O portal de volta nascia dentro da arquibancada. Ele era posto ao lado do ponto de chegada da plateia, sem piso reservado, e caía dentro do degrau de cima. Agora há um patamar próprio na ponta do primeiro degrau, e o portal fica em cima dele.
- A entrada da torre passa a aparecer no mapa — pino na base, no mundo, não na sala a 3200 m: é a base que tem o portal e é para lá que se anda.
- "36 × 34 andável" é caixa envolvente, não retângulo — e eu li como retângulo. Escolhi a sala maior por esse número e pus o ponto de chegada do lutador no meio dela, que na
Hall_3(forma irregular, uma boca só) é vazio. O sintoma não foi cair: foi o teleporte nunca terminar — o jogo seguraIsTeleportingaté assentar o jogador — e, com ele preso,Character.Damagedescartar todo dano. A luta ficava eterna com os dois intactos. O ringue passa a ser pavimentado com peça vanilla, o que independe do formato interno da sala; a sala virou casca e o chão é nosso. - Duas afirmações novas travam essa classe de erro: existe piso sólido sob o lutador, e o teleporte terminou antes de o combate valer.
- Peças da cidade não afundam mais no terreno. Medido: 26 de 56 com a base abaixo do chão, o portal enterrado 1,01 m dos seus 3,29 m de altura. A causa é que a cidade nivela e coloca as peças no mesmo frame — o heightmap só aplica depois, e o nivelamento alisa em vez de aplainar, então o terreno final fica até 1 m acima da altura usada. Entra uma passada que assenta as peças sobre o terreno real, só levantando quem está enterrado: peça acima do chão pode estar assim de propósito (arquibancada, sacada, telhado). Resultado medido: 0 de 56.
- Como o realinhamento dos moradores, essa passada alcança cidades que já existem — inclusive a do servidor — e se auto-corrige se o terreno mudar depois.
- A torre virou um coliseu de peça vanilla e o OdinsHollow saiu do caminho. Até aqui a torre simplesmente não nascia sem aquele mod instalado — e foi ele que quebrou o menu do martelo dois dias atrás. Agora a estrutura é levantada peça a peça: ringue de 24 × 24 m pavimentado, três degraus nos quatro lados (antes eram dois lados, porque a sala pronta era retangular), grade de ferro vazada em todo o perímetro e parede externa de duas fileiras atrás do último degrau — sem ela a arquibancada ficaria pendurada no vazio a 3200 m. Sem teto, que é o que faz um coliseu parecer um coliseu. 444 peças.
- Do ringue, a grade é uma parede de 4 m: não se pula. E é vazada, então a plateia assiste — parede cega tiraria o motivo de existir a arquibancada.
iron_gratetem o pivô 1 m acima da base, e eu tinha colocado as grades enterradas. O mesmo defeito que eu diagnostiquei nas cidades, repetido na arena por medir o pivô de umas peças e não de outras. Toda peça do coliseu passa a ser colocada pela base, com o pivô medido no jogo — a conta vira "onde eu quero que ela encoste". A tabelaPivotAboveBottomescrita à mão (40 e poucas peças, consultada só na cerca, errada para várias) deixa de ser a fonte.- A torre parava de lugar a cada reconstrução — das duas últimas, de (124,40,386) para (270,33,1157). Quem já sabia onde ela ficava perdia a referência a cada atualização. Agora, havendo torre gravada, a nova nasce no mesmo lugar; só um
rebuildexplícito reescolhe o ponto. - A torre anterior passa a ser demolida antes de a nova subir. Cada geração nova deixava a anterior de pé: a sala fica a 3200 m, numa zona que ninguém carrega, e a varredura por objetos de cena não a alcançava.
ZDOMan.FindSectorObjectsenumera os ZDOs do setor mesmo sem objeto instanciado — e como o setor é 2D, uma varredura em volta da posição antiga pega o exterior no chão e a sala lá em cima de uma vez. Na verificação: 115 e 491 peças órfãs removidas. - O assentamento de peças passou a alcançar os acampamentos de facção. Eles têm o mesmo defeito das cidades (
FactionCamps.Foundtambém nivela e coloca no mesmo frame) e marcam as peças igual, mas a passada estava ancorada em cidade e não os via. Agora ancora no jogador, o que cobre cidade, acampamento e o que vier depois. Acampamento não recebe verificação de simetria de anel: não tem cerca. - As oito cidades passaram a ser verificadas de verdade, e não por extrapolação. Até aqui eu media uma cidade e afirmava as oito "por construção" — o usuário foi ver e achou três quebradas. Entra uma ferramenta que ergue o cercado de cada um dos oito tiers ao lado do jogador, em terreno natural (o caso difícil, não o nivelado), mede e remove: contagem contra a largura medida daquele prefab, peças flutuando, enterradas e tortas. Dez segundos por tier, sem fundar cidade nenhuma. Resultado: 36, 52, 52, 96, 36, 48, 48, 48 — todos exatos, zero flutuando, zero torta.
- A causa das cidades quebradas era uma linha, repetida em três lugares:
Vector3.Distancepara filtrar um anel horizontal. Em terreno inclinado as peças ficam metros acima ou abaixo do centro e caem fora do raio esférico — então a limpeza não as removia (e o anel novo entrava em cima: 81, 133 e 155 peças onde cabem 36, 51 e 96) e a medição não as contava (acusou "16 de 36" num anel completo). O mesmo erro nas duas pontas, com sintomas opostos. Todas as três passaram a usar distância no plano, por uma função só. - Isso explica a ordem de gravidade observada: a cidade da montanha é o terreno mais inclinado do jogo, mais peças sobrevivem à limpeza, mais anéis empilham. Era a "completamente quebrada".
- A cidade do pântano nunca nascia, e não era falta de carregamento.
FindFlatSpotexigia que todas as nove amostras de um quadrado de 10 m estivessem acima da água; no pântano quase todo ponto tem poça a menos de 5 m, então nenhum candidato sobrevivia em nenhum anel. A exigência era rígida demais porque a cidade nivela o terreno depois — célula molhada na borda sobe junto. Agora: centro seco obrigatório, no máximo duas células molhadas na volta, e entre dois pontos igualmente planos ganha o mais seco. A mensagem de erro dizia "terreno ainda não carregado" e me mandou para o lugar errado duas vezes; passou a dizer o que de fato acontece. - O cercado se refaz sozinho quando a contagem está errada — nas duas direções. Eu só verificava se faltava peça; o problema real era sobrar. Cidade marcada com a geração atual e 190 peças nunca era refeita, porque "está atual?" e "está incompleta?" eram as duas únicas perguntas.
- Sem chão legível, o construtor não coloca a peça. A reserva antiga usava a altura do último ponto nivelado, que pode apontar para outro lugar do mundo — peças iam para uma altura alheia e ficavam fora do próprio anel. Peça faltando é melhor que peça no lugar errado: a faltante é detectada pela contagem e reposta.
- O assentamento alcança os acampamentos de facção. Eles têm o mesmo defeito das cidades e marcam as peças igual, mas a passada estava ancorada em cidade e não os via. Agora ancora no jogador. Acampamento não recebe verificação de simetria de anel: não tem cerca.
- O piloto automático salva o mundo antes de encerrar. O auto-save do jogo é a cada 30 min e o script encerra o processo à força: rodadas de 20 minutos que fundavam seis cidades jogavam tudo fora, e a rodada seguinte começava do zero sem explicação aparente.
- A medição de peça passou a ser determinística, lendo a geometria do colisor em vez da caixa no mundo.
Collider.boundsé a caixa no MUNDO e depende do estado da física no instante da leitura: a mesmapiece_sharpstakesdeu contagem esperada 52 numa rodada e 48 na seguinte. Contagem de anel que oscila entre sessões significa cidade do mesmo tier com anéis diferentes, e a auto-recuperação por contagem errada alternando para sempre entre dois valores. Agora a medida vem dosizedo colisor, doboundsda malha ou do raio da esfera, transformados pelos oito cantos até o espaço da raiz do prefab (necessário porque a peça pode estar girada dentro dele) -- dado do prefab, não estado de runtime. E nada é instanciado para medir. Verificado com duas rodadas completas: larguras idênticas e as oito contagens exatas nas duas. - A tabela
PivotAboveBottomescrita à mão ficou obsoleta: ela discorda da medição em vários casos (dizia 0,15 para awood_fence, a medida dá 0,13). Só é consultada quando o prefab não existe, então o risco é baixo, mas ela deve sair.
0.25.97 — 2026-09-16
- O portal da torre passa a funcionar. Ele era decorativo: o
SpawnPortalmarcava o ZDO como portal de cidade, e o patch de cidade intercepta qualquer portal com essa marca para dizer "este portal é mantido pela cidade" — então a única entrada da arena era por comando. Agora a peça sai com a marca da arena e mais nada, e quem a trata é o patch próprio. - Entra um portal de volta atrás da plateia, e o teleporte é feito pelo mod nos dois sentidos, com a direção saindo da altitude: no chão entra, a 3200 m sai. Não se usa o pareamento vanilla por tag porque com dois portais de mesma tag o jogo liga um ao outro por conta, e qual liga em qual depende da ordem de carregamento — foi a lição que a raid já tinha aprendido.
Historico anterior omitido por limite de tamanho da Thunderstore. O changelog completo esta no repositorio do mod.