[TFS 1.x+] How to NOT write LUA scripts or how to crash server by LUA script

There are many tutorials about writing scripts for TFS 1.x, so I decided to write short tutorial how to NOT write scripts for TFS 1.x.
In this tutorial I will describe two common mistakes that result in server crash!

First thing that everyone notice when they change engine from TFS 0.x to 1.x are objects in LUA.
There are many of them, but most popular are: Player, Monster, Npc, Creature, Tile, Item, Container, Position

Thing that most scripters don’t know, is that, these objects are directly bind to address in RAM memory (to C++ objects).
If you try to use C++ object (ex. get name of player) that was deleted, it results in server crash. Bad thing about it is: you cannot check, if object was deleted in C++!
C++ programmers are used to take care of not using deleted objects. LUA scripters in all old engines always get nice error message, when they use variable that does not exist (ex. Creature not found).

There are 2 common mistakes that result in crash. Worst thing about them is that, they don’t crash server always. You can write code like that, test it, put it on your server, many players will use it.. and after week you get random crash.

PROBLEM 1

Passing object to addEvent – addEvent is function that execute given function with some delay. Example – print text after 5 seconds:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function executeMeLater()
print('printed after 5 seconds!')
end
addEvent(executeMeLater, 5000)
function executeMeLater() print('printed after 5 seconds!') end addEvent(executeMeLater, 5000)
function executeMeLater()
    print('printed after 5 seconds!')
end

addEvent(executeMeLater, 5000)

You can pass arguments to function executed with delay:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function executeMeLater(param1, param2)
print(param1)
print(param2)
end
addEvent(executeMeLater, 5000, 'test string', 12345)
function executeMeLater(param1, param2) print(param1) print(param2) end addEvent(executeMeLater, 5000, 'test string', 12345)
function executeMeLater(param1, param2)
    print(param1)
    print(param2)
end

addEvent(executeMeLater, 5000, 'test string', 12345)

It will print after 5 seconds:

test string
12345

What is a problem with passing object to it? Object – in RAM memory – can be already deleted!
Example talkaction that crash server:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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

What it does, if player stays online for 5 seconds after executing this talkaction:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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

As you can see, there is a simple protection against passing objects as arguments to addEvent. Passing it directly as argument, results in changing object Player to old ‘cid’ version (player temporary ID number).
In second event example I put object Player inside table to bypass protection code. That way I could use ‘:getName()’ on object, as it was not converted to ‘cid’ number.

What it does, if player relog within 5 seconds after executing this talkaction:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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)
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)

SERVER CRASHED!
It tried to load passed Player object (use ‘:getName()’ on it). Player did relog, so given instance of Player was removed in C++.
gdb report after crash:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#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() ()
#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() ()
#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() ()

PROBLEM 2

Keeping object in variable for future use in script.
There is example of common usage in script – keeping last player who executed script (who went into some room, who used some lever):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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

You can execute this code as many times you want and it will work fine.. until you relog or player that used it as last, logout and someone else execute it.
gdb report after crash:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#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.
#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.
#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.

How to make scripts safe? Pass cid/guid/itemid/position, not object.
Problem 1 script – safe version:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function buggedEvent1(player)
print('Execute bugged event 1')
print(player)
end
function buggedEvent2(hiddenPlayerObject)
print('Execute bugged event 2')
-- FIX: TRY TO CREATE OBJECT 'PLAYER' FROM IT'S CID
local myPlayer = Player(hiddenPlayerObject.myPlayerVariable)
-- FIX: IT WILL CREATE OBJECT 'PLAYER' OR RETURN 'nil'
if myPlayer then
-- FIX: 'if' CHECKED THAT 'myPlayer' IS NOT 'nil',
-- SO CREATING OBJECT 'PLAYER' WORKED
-- WE CAN USE '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) OF PLAYER, NOT OBJECT
local hiddenPlayerObject = { myPlayerVariable = player:getId() }
addEvent(buggedEvent2, 5000, hiddenPlayerObject)
return true
end
function buggedEvent1(player) print('Execute bugged event 1') print(player) end function buggedEvent2(hiddenPlayerObject) print('Execute bugged event 2') -- FIX: TRY TO CREATE OBJECT 'PLAYER' FROM IT'S CID local myPlayer = Player(hiddenPlayerObject.myPlayerVariable) -- FIX: IT WILL CREATE OBJECT 'PLAYER' OR RETURN 'nil' if myPlayer then -- FIX: 'if' CHECKED THAT 'myPlayer' IS NOT 'nil', -- SO CREATING OBJECT 'PLAYER' WORKED -- WE CAN USE '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) OF PLAYER, NOT OBJECT local hiddenPlayerObject = { myPlayerVariable = player:getId() } addEvent(buggedEvent2, 5000, hiddenPlayerObject) return true end
function buggedEvent1(player)
   print('Execute bugged event 1')
   print(player)
end

function buggedEvent2(hiddenPlayerObject)
   print('Execute bugged event 2')
   -- FIX: TRY TO CREATE OBJECT 'PLAYER' FROM IT'S CID
   local myPlayer = Player(hiddenPlayerObject.myPlayerVariable)
   -- FIX: IT WILL CREATE OBJECT 'PLAYER' OR RETURN 'nil'
   if myPlayer then
      -- FIX: 'if' CHECKED THAT 'myPlayer' IS NOT 'nil',
      -- SO CREATING OBJECT 'PLAYER' WORKED
      -- WE CAN USE '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) OF PLAYER, NOT OBJECT
   local hiddenPlayerObject = { myPlayerVariable = player:getId() }
   addEvent(buggedEvent2, 5000, hiddenPlayerObject)
   return true
end

Problem 2 script – safe version:
Version with player ID (in old scripts called ‘cid’). It’s temporary player ID. It changes when player relog/die:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
local lastPlayerCid = nil
function onSay(player, words, param)
print('onSay, player name: ' .. player:getName())
-- FIX: TRY TO CREATE OBJECT 'PLAYER' FROM IT'S CID
local lastPlayer = Player(lastPlayerCid)
-- FIX: IT WILL CREATE OBJECT 'PLAYER' OR RETURN 'nil'
if lastPlayer then
print('onSay, last player name: ' .. lastPlayer:getName())
end
-- FIX: STORE ID OF PLAYER (cid), NOT OBJECT
lastPlayerCid = player:getId()
return true
end
local lastPlayerCid = nil function onSay(player, words, param) print('onSay, player name: ' .. player:getName()) -- FIX: TRY TO CREATE OBJECT 'PLAYER' FROM IT'S CID local lastPlayer = Player(lastPlayerCid) -- FIX: IT WILL CREATE OBJECT 'PLAYER' OR RETURN 'nil' if lastPlayer then print('onSay, last player name: ' .. lastPlayer:getName()) end -- FIX: STORE ID OF PLAYER (cid), NOT OBJECT lastPlayerCid = player:getId() return true end
local lastPlayerCid = nil

function onSay(player, words, param)
   print('onSay, player name: ' .. player:getName())
   -- FIX: TRY TO CREATE OBJECT 'PLAYER' FROM IT'S CID
   local lastPlayer = Player(lastPlayerCid)
   -- FIX: IT WILL CREATE OBJECT 'PLAYER' OR RETURN 'nil'
   if lastPlayer then
      print('onSay, last player name: ' .. lastPlayer:getName())
   end
   -- FIX: STORE ID OF PLAYER (cid), NOT OBJECT
   lastPlayerCid = player:getId()
   return true
end

Version with player GUID. It’s player ID from database. It never changes.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
local lastPlayerGuid = nil
function onSay(player, words, param)
print('onSay, player name: ' .. player:getName())
-- FIX: TRY TO CREATE OBJECT 'PLAYER' FROM IT'S GUID
local lastPlayer = Player(lastPlayerGuid)
-- FIX: IT WILL CREATE OBJECT 'PLAYER' OR RETURN 'nil'
if lastPlayer then
print('onSay, last player name: ' .. lastPlayer:getName())
end
-- FIX: STORE ID OF PLAYER (cid), NOT OBJECT
lastPlayerGuid = player:getGuid()
return true
end
local lastPlayerGuid = nil function onSay(player, words, param) print('onSay, player name: ' .. player:getName()) -- FIX: TRY TO CREATE OBJECT 'PLAYER' FROM IT'S GUID local lastPlayer = Player(lastPlayerGuid) -- FIX: IT WILL CREATE OBJECT 'PLAYER' OR RETURN 'nil' if lastPlayer then print('onSay, last player name: ' .. lastPlayer:getName()) end -- FIX: STORE ID OF PLAYER (cid), NOT OBJECT lastPlayerGuid = player:getGuid() return true end
local lastPlayerGuid = nil

function onSay(player, words, param)
   print('onSay, player name: ' .. player:getName())
   -- FIX: TRY TO CREATE OBJECT 'PLAYER' FROM IT'S GUID
   local lastPlayer = Player(lastPlayerGuid)
   -- FIX: IT WILL CREATE OBJECT 'PLAYER' OR RETURN 'nil'
   if lastPlayer then
      print('onSay, last player name: ' .. lastPlayer:getName())
   end
   -- FIX: STORE ID OF PLAYER (cid), NOT OBJECT
   lastPlayerGuid = player:getGuid()
   return true
end

There are 2 main reasons for using ID version:

  • It’s sometimes useful to know that player did relog/die (change ID). If he went to no-logout zone and his ID changed, it means he died.
  • It also works for Monster, Npc and Creature objects. You can store ID of monster/npc/player and load it same way – some LUA scripts work for monsters (ex. movements).

There is one reason for using GUID:

  • Some scripts need to know if player is online, even if he did relog.

Leave a Reply

Your email address will not be published. Required fields are marked *