Administrador gutinha 1.280 Postado 27 de Setembro 2023 Administrador Compartilhar Postado 27 de Setembro 2023 Existem muitos tutoriais sobre como escrever scripts para o TFS 1.x, então decidi escrever um breve tutorial sobre como NÃO escrever scripts para o TFS 1.x. Neste tutorial irei descrever dois erros comuns que resultam em travamento do servidor! A primeira coisa que todos notam quando mudam o mecanismo do TFS 0.x para 1.x são os objetos no LUA. Existem muitos deles, mas os mais populares são: Jogador, Monstro, Npc, Criatura, Bloco, Item, Recipiente, Posição. O que a maioria dos criadores de scripts não sabe é que esses objetos são vinculados diretamente ao endereço na memória RAM (a objetos C++). Se você tentar usar o objeto C++ (por exemplo, obter o nome do jogador) que foi excluído, isso resultará em crash do servidor. O ruim disso é: você não pode verificar se o objeto foi excluído em C++! Os programadores C++ estão acostumados a tomar cuidado para não usar objetos excluídos. Os criadores de scripts LUA em todos os mecanismos antigos sempre recebem uma boa mensagem de erro quando usam uma variável que não existe (por exemplo, Criatura não encontrada). Existem 2 erros comuns que resultam em travamento. A pior coisa sobre eles é que nem sempre travam o servidor. Você pode escrever um código como esse, testá-lo, colocá-lo em seu servidor, muitos jogadores irão usá-lo... e depois de uma semana você terá um travamento aleatório. 1° Problema Spoiler Passando objeto para addEvent - addEvent é uma função que executa determinada função com algum atraso. Exemplo - imprima o texto após 5 segundos: function executeMeLater() print('printed after 5 seconds!') end addEvent(executeMeLater, 5000) Você pode passar argumentos para funções executadas com atraso: function executeMeLater(param1, param2) print(param1) print(param2) end addEvent(executeMeLater, 5000, 'test string', 12345) Irá imprimir: test string 12345 Depois de 5 segundos. Qual é o problema em passar objeto para ele? Objeto - na memória RAM - já pode ser excluído! Exemplo de talkaction que trava o servidor: function buggedEvent1(player) print('Execute bugged event 1') print(player) end function buggedEvent2(hiddenPlayerObject) print('Execute bugged event 2') print(hiddenPlayerObject.myPlayerVariable:getName()) end function onSay(player, words, param) print('onSay, player name: ' .. player:getName()) -- TRY 1 addEvent(buggedEvent1, 4000, player) -- TRY 2 local hiddenPlayerObject = { myPlayerVariable = player } addEvent(buggedEvent2, 5000, hiddenPlayerObject) return true end O que acontece se o jogador permanecer online por 5 segundos após executar esta talkaction: onSay, player name: Gesior Lua Script Error: [TalkAction Interface] data/talkactions/scripts/crashAddEvent.lua:onSay luaAddEvent(). Argument #3 is unsafe stack traceback: [C]: in function 'addEvent' data/talkactions/scripts/crashAddEvent.lua:14: in function <data/talkactions/scripts/crashAddEvent.lua:11> Execute bugged event 1 268435457 Execute bugged event 2 Gesior Como você pode ver, existe uma proteção simples contra a passagem de objetos como argumentos para addEvent. Passá-lo diretamente como argumento resulta na mudança do objeto Player para a versão antiga 'cid' (número de ID temporário do jogador). No segundo exemplo de evento coloquei o objeto Player dentro da tabela para ignorar o código de proteção. Dessa forma eu poderia usar ':getName()' no objeto, pois não foi convertido para o número 'cid'. O que acontece se o jogador fizer login novamente dentro de 5 segundos após executar esta ação de conversação: onSay, player name: Gesior Lua Script Error: [TalkAction Interface] data/talkactions/scripts/crashAddEvent.lua:onSay luaAddEvent(). Argument #3 is unsafe stack traceback: [C]: in function 'addEvent' data/talkactions/scripts/crashAddEvent.lua:14: in function <data/talkactions/scripts/crashAddEvent.lua:11> Gesior has logged out. Execute bugged event 1 268435456 Execute bugged event 2 Segmentation fault (core dumped) SERVIDOR CRASHADO! Ele tentou carregar o objeto Player passado (use ':getName()' nele). O Player relogou, então determinada instância do Player foi removida em C++. relatório gdb após falha: #0 0x0000000000000000 in ?? () No symbol table info available. #1 0x000055d4fed50615 in LuaScriptInterface::luaCreatureGetName(lua_State*) () No symbol table info available. #2 0x00007f8865d55e37 in ?? () from /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2 No symbol table info available. #3 0x00007f8865da327c in lua_pcall () from /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2 No symbol table info available. #4 0x000055d4fedc9274 in LuaScriptInterface::protectedCall(lua_State*, int, int) () No symbol table info available. #5 0x000055d4fedca368 in LuaScriptInterface::callFunction(int) () No symbol table info available. #6 0x000055d4fed39af2 in LuaEnvironment::executeTimerEvent(unsigned int) () No symbol table info available. #7 0x000055d4fed1eb6e in void std::__invoke_impl<void, void (LuaEnvironment::*&)(unsigned int), LuaEnvironment*&, unsigned int&>(std::__invoke_memfun_deref, void (LuaEnvironment::*&)(unsigned int), LuaEnvironment*&, unsigned int&) () No symbol table info available. #8 0x000055d4fed1bcac in std::__invoke_result<void (LuaEnvironment::*&)(unsigned int), LuaEnvironment*&, unsigned int&>::type std::__invoke<void (LuaEnvironment::*&)(unsigned int), LuaEnvironment*&, unsigned int&>(void (LuaEnvironment::*&)(unsigned int), LuaEnvironment*&, unsigned int&) () No symbol table info available. #9 0x000055d4fed16be2 in void std::_Bind<void (LuaEnvironment::*(LuaEnvironment*, unsigned int))(unsigned int)>::__call<void, , 0ul, 1ul>(std::tuple<>&&, std::_Index_tuple<0ul, 1ul>) () No symbol table info available. #10 0x000055d4fed1048d in void std::_Bind<void (LuaEnvironment::*(LuaEnvironment*, unsigned int))(unsigned int)>::operator()<, void>() () No symbol table info available. #11 0x000055d4fed09916 in std::_Function_handler<void (), std::_Bind<void (LuaEnvironment::*(LuaEnvironment*, unsigned int))(unsigned int)> >::_M_invoke(std::_Any_data const&) () No symbol table info available. #12 0x000055d4febfaec8 in std::function<void ()>::operator()() const () No symbol table info available. #13 0x000055d4febfa9fe in Task::operator()() () No symbol table info available. #14 0x000055d4febfac2c in Dispatcher::threadMain() () 2° Problema Spoiler Manter o objeto em variável para uso futuro no script. Há um exemplo de uso comum em script - manter o último jogador que executou o script (que entrou em alguma sala, que usou alguma alavanca): local lastPlayer = nil function onSay(player, words, param) print('onSay, player name: ' .. player:getName()) if lastPlayer ~= nil then print('onSay, last player name: ' .. lastPlayer:getName()) end lastPlayer = player return true end Você pode executar este código quantas vezes quiser e ele funcionará bem.. até que você relogue ou o jogador que o usou por último, saia e outra pessoa o execute. relatório gdb após falha: #0 0x00007ff55a471d20 in ?? () No symbol table info available. #1 0x000055dffa717615 in LuaScriptInterface::luaCreatureGetName(lua_State*) () No symbol table info available. #2 0x00007ff560fd0e37 in ?? () from /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2 No symbol table info available. #3 0x00007ff56101e27c in lua_pcall () from /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2 No symbol table info available. #4 0x000055dffa790274 in LuaScriptInterface::protectedCall(lua_State*, int, int) () No symbol table info available. #5 0x000055dffa791368 in LuaScriptInterface::callFunction(int) () No symbol table info available. #6 0x000055dffa5f8554 in TalkAction::executeSay(Player*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, SpeakClasses) const () No symbol table info available. #7 0x000055dffa5f81c1 in TalkActions::playerSaySpell(Player*, SpeakClasses, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const () No symbol table info available. #8 0x000055dffa827c4f in Game::playerSaySpell(Player*, SpeakClasses, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) () No symbol table info available. #9 0x000055dffa827910 in Game::playerSay(unsigned int, unsigned short, SpeakClasses, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) () No symbol table info available. Como tornar os scripts seguros? Passe cid/guid/itemid/position, não objeto. Script do Problema 1 - versão segura: Spoiler function buggedEvent1(player) print('Execute bugged event 1') print(player) end function buggedEvent2(hiddenPlayerObject) print('Execute bugged event 2') -- FIX: TENTE CRIAR OBJETO 'PLAYER' A PARTIR DE SEU CID local myPlayer = Player(hiddenPlayerObject.myPlayerVariable) -- FIX: IRÁ CRIAR O OBJETO 'PLAYER' OU RETORNAR 'nil' if myPlayer then -- FIX: 'if' VERIFICOU QUE 'myPlayer' NÃO É 'nil', -- ASSIM CRIAR O OBJETO 'PLAYER' FUNCIONOU -- PODEMOS USAR 'getName' print(myPlayer:getName()) end end function onSay(player, words, param) print('onSay, player name: ' .. player:getName()) -- TRY 1 addEvent(buggedEvent1, 4000, player) -- TRY 2 -- FIX: PASS ID (cid) DO JOGADOR, NÃO DO OBJETO local hiddenPlayerObject = { myPlayerVariable = player:getId() } addEvent(buggedEvent2, 5000, hiddenPlayerObject) return true end Script do Problema 2 - versão segura: Versão com ID do jogador (em scripts antigos chamados 'cid'). É um ID de jogador temporário. Ele muda quando o jogador relog/morre: Spoiler local lastPlayerCid = nil function onSay(player, words, param) print('onSay, player name: ' .. player:getName()) -- FIX: TENTE CRIAR OBJETO 'PLAYER' A PARTIR DE SEU CID local lastPlayer = Player(lastPlayerCid) -- FIX: IRÁ CRIAR O OBJETO 'PLAYER' OU RETORNAR 'nil' if lastPlayer then print('onSay, last player name: ' .. lastPlayer:getName()) end -- FIX: ARMAZENAR ID DO JOGADOR (cid), NÃO OBJETO lastPlayerCid = player:getId() return true end Versão com GUID do player. É o ID do jogador do banco de dados. Isso nunca muda. local lastPlayerGuid = nil function onSay(player, words, param) print('onSay, player name: ' .. player:getName()) -- FIX: TENTE CRIAR OBJETO 'PLAYER' A PARTIR DO GUID local lastPlayer = Player(lastPlayerGuid) -- FIX: IRÁ CRIAR O OBJETO 'PLAYER' OU RETORNAR 'nil' if lastPlayer then print('onSay, last player name: ' .. lastPlayer:getName()) end -- FIX: ARMAZENAR ID DO JOGADOR (cid), NÃO OBJETO lastPlayerGuid = player:getGuid() return true end Existem 2 razões principais para usar a versão do ID: Às vezes é útil saber que o jogador relogou/morreu (alterou ID). Se ele foi para a zona sem logout e seu ID mudou, significa que ele morreu. Também funciona para objetos Monstro, Npc e Criatura. Você pode armazenar o ID do monstro/npc/player e carregá-lo da mesma maneira - alguns scripts LUA funcionam para monstros (ex. movements). Há um motivo para usar o GUID: - Alguns scripts precisam saber se o jogador está online, mesmo que ele tenha relogado. Créditos: Gesior.pl 2 Citar Link para o comentário Compartilhar em outros sites Mais opções de compartilhamento...
Administrador TELASKO 2.926 Postado 28 de Setembro 2023 Administrador Compartilhar Postado 28 de Setembro 2023 muito bom ! Citar Link para o comentário Compartilhar em outros sites Mais opções de compartilhamento...
Moderador Wang 1.338 Postado 28 de Setembro 2023 Moderador Compartilhar Postado 28 de Setembro 2023 muuto bom recomendo Citar Link para o comentário Compartilhar em outros sites Mais opções de compartilhamento...
Posts Recomendados
Participe da Conversa
Você pode postar agora e se cadastrar mais tarde. Cadastre-se Agora para publicar com Sua Conta.