User Tag List

Page 1 of 2 1 2 LastLast
Results 1 to 7 of 12

Thread: Lua tips and tricks

  1. #1
    Moderator / Eluna Dev <span style='color: #FF0000'><span class='glow_8B0000'>Rochet2</span></span>'s Avatar
    Join Date
    Apr 2013
    Location
    Finland
    Posts
    1,859
    XP
    13,487
    Level
    35
    Thanks
    95
    Thanked 591 Times in 418 Posts

    Lua tips and tricks

    luathread
    I thought to post a little thread about scripting with lua.
    Posting some special things you can, should and should not do and why.

    I apologize if I appear messy or not understandable at some points : |

    This thread has nice comments and tips in the replies as well!
    Do remember to read them as well :)



    NEVER save an ?object? overtime! (boss = unit is a no no)
    Ok, .. there are some exceptions.. (see next post for an example of a nice workaround :))
    But basically you should never save a player for example to a variable you are not using instantly.
    When the player logs out, the variable wont become nil. On C++ side the player is a pointer, which will be "valid" (not NULL), but it points to an area that no longer has player data in it.
    This means that when a player logs out and you use the player object still after that, there is a possibility for crashing and having bad method reads and more.
    This problem can be avoided in certain ways, but I would rather not use them as they would disable certain functionality and possibilities. Be smart, dont do it :)
    Should use guids instead. (GetGUID)

    Use GetGUIDLow! not GetGUID
    This may sound a little like I am contradicting myself with what I said before..
    But GUID is a (number as a) string, and as such, it is "worse" for tables as a key.
    It is also bigger than GUIDLow, and it also can not and should not be used in the database.
    When handling stuff in the code, it is fine to use GUID instead of GUIDLow though. GUIDLow is the ID used in database tables for player, creature etc!
    Check out the differences with GUID and low GUID :) Also see the global methods for them

    Collision in scripts is seen very often.
    Note that the script is not bound to a creature. It is a global script.
    This means that if you use a variable like:
    local spawned = false
    It is just one variable. This means that multiple NPCs that run an event to change or check that variable use the same variable.
    So, for example if a boss script that sets that variable to true when spawning adds, other instances of the same raid or dungeon where the boss is wont get adds at all, since the variable says that they are already spawned when checked by the script.
    You should solve this problem for example by saving data to a lua table with the instance ID or creature guid low for example (boss guid)

    Localize your variables and functions!
    You should always localize your variables. Mostly you can just say local x = 2 and so, but sometimes you may need to declare a variable local before using it.
    This can be achieved by doing for example:
    Code:
    local testvar
    if(condition) then
     testvar = true
    else
     testvar = false
    end
    This code will set a local variable, but you wont hit a problem with the variable being local like when trying similar to this:
    Code:
    if(condition) then
     local testvar = true
    else
     local testvar = false
    end
    -- testvar is nil here :(
    Localizing your functions may be important.
    You can do this in a few ways. One of the ways is to just declare that it is local when defining or declaring the function like so:
    local function testFunction()
    With this way you may run into problems when trying to use the function that is local before it is defined though.
    You can solve this problem by using the option 2 or by declaring the functions local before using them, like this:
    Code:
    local testFunc
    -- code
    function testFunction() -- code
    Another way is to store all functions (and variables) inside a table, which then can be made local.
    Code:
    local T = {}
    function T.MyFunction()
       -- code
    end
    T.SomeVar = false
    Localizing variables and functions is needed to prevent them from being overwritten or from overwriting functions and variables with the same name.
    They also wont take up memory by being global when used just in one function for example.
    Often scripts tend to use unique function names and as such, mostly people do not use local functions. Local functions are said to be faster though. (source?)

    What if I need to pass more variables than a function can receive :(?
    In some situations you might want to pass additional arguments to functions.
    This is often needed with timed events. This is also a useful trick for some situations :)
    Code:
    local function myFunction(eventId, delay, calls, customVar)
        print(customVar) -- prints 123
    end
    CreateLuaEvent(function(a,b,c) myFunction(a,b,c, 123) end, 5000, 0)
    Lua allows you to create functions inside a function. You can also store functions to a variable and tables and so.
    Actually the function defining syntax is just a syntactic sugar to:
    local myFunction = function(eventId, delay, calls, customVar) print(customVar) end
    Last edited by Rochet2; 01-25-2014 at 07:24 PM.
    Awards:
    Support LOL Scripter Facepalm! Raffle Winner

  2. The Following 10 Users Say Thank You to Rochet2 For This Useful Post:


  3. #2
    Enthusiast SkittlesAreFalling's Avatar
    Join Date
    Aug 2013
    Posts
    42
    XP
    2,396
    Level
    13
    Thanks
    10
    Thanked 2 Times in 2 Posts
    Can just use (for example, not what I use exactly) something like Player[ID].WorldObjectVar = nil when they logout, like I do similarly, but okay. Lua gets rid of nil variables from memory so setting all player-linked variables to nil when the player logs out should be fine.

    The rest I pretty much already do but thanks for the advice, glad to hear I'm doing things right.
    Achievements:
    1 year registered Member Level X

  4. The Following User Says Thank You to SkittlesAreFalling For This Useful Post:


  5. #3
    Original Poster
    Moderator / Eluna Dev <span style='color: #FF0000'><span class='glow_8B0000'>Rochet2</span></span>'s Avatar
    Join Date
    Apr 2013
    Location
    Finland
    Posts
    1,859
    XP
    13,487
    Level
    35
    Thanks
    95
    Thanked 591 Times in 418 Posts
    Well sure you can do that i guess.. Hmm.
    Not sure if there are some exceptions to logging off.

    Sadly we cant exactly implement that as a default thing to do.
    Or we /might/ possibly in some way .. hmmmm This would mean that when calling a method on the object the object would be nil.
    We would likely need to implement this to destructor of object to make it work nicely.
    But it wouldnt really be efficient. Best way is to do as you suggested or use guids if you really need to save a player overtime.
    Last edited by Rochet2; 01-25-2014 at 09:54 AM.
    Awards:
    Support LOL Scripter Facepalm! Raffle Winner

  6. #4
    Enthusiast efonius's Avatar
    Join Date
    Jan 2014
    Posts
    54
    XP
    2,254
    Level
    13
    Thanks
    18
    Thanked 4 Times in 4 Posts
    Great write up, just to see some solid example of how to localize, could you tell me what you would have to change to localize this example script to work in a way that multiple people could run/do it at the same time?

  7. #5
    Original Poster
    Moderator / Eluna Dev <span style='color: #FF0000'><span class='glow_8B0000'>Rochet2</span></span>'s Avatar
    Join Date
    Apr 2013
    Location
    Finland
    Posts
    1,859
    XP
    13,487
    Level
    35
    Thanks
    95
    Thanked 591 Times in 418 Posts
    Quote Originally Posted by efonius View Post
    Great write up, just to see some solid example of how to localize, could you tell me what you would have to change to localize this example script to work in a way that multiple people could run/do it at the same time?
    Well, as said in my main post, the easy ways would be to create a table and then using T.FuncName instead of FuncName everywhere.
    And then have the table local.
    Or then you could make a list of all functions to the top of the script like here on line 23:
    http://pastebin.com/uqctudvj
    See LoadDB for example. It is declared local at the top, but then when defined a few rows below, it is used normally.

    Another thing are the variables like currentWP.
    You should create a table and store those for the creature.
    local T = {}
    Then when used in code:
    T[creature:GetGUIDLow()] = currentWP+1;

    Hmm, we should probably update those scripts .. they are terribly outdated :P
    They wouldnt work at all on the engine today.
    Last edited by Rochet2; 01-25-2014 at 04:54 PM.
    Awards:
    Support LOL Scripter Facepalm! Raffle Winner

  8. The Following User Says Thank You to Rochet2 For This Useful Post:


  9. #6
    Original Poster
    Moderator / Eluna Dev <span style='color: #FF0000'><span class='glow_8B0000'>Rochet2</span></span>'s Avatar
    Join Date
    Apr 2013
    Location
    Finland
    Posts
    1,859
    XP
    13,487
    Level
    35
    Thanks
    95
    Thanked 591 Times in 418 Posts
    Know you hooks!
    Hooks trigger at different parts of the code.
    So for example XP receive hook will trigger BEFORE adding the XP to the player.
    This is due to C++ being able to edit the XP given to player in the script (coming to eluna soon :))
    There are some hooks that trigger before some event, some trigger after it and some trigger in the middle of the code.

    For example, the OnFirstLogin hook executes a bit in the middle of the code, right before OnFirstLogin flag is removed from the player. Not before login, not after it.
    Character create hook also gives you access to the player object, but dont expect for example to be able to send messages to the player. The player is only a temporary object and has not yet logged in even.

    This means that if some method or code doesnt work ..(for example I think salja tried to add creature kills to a quest on quest accept and the script didnt work since the hook probably executes before the player has the quest)
    .. test a bit and think of what you are trying to do and when.

  10. #7
    Enthusiast SkittlesAreFalling's Avatar
    Join Date
    Aug 2013
    Posts
    42
    XP
    2,396
    Level
    13
    Thanks
    10
    Thanked 2 Times in 2 Posts
    Made a "function library" for other scripts to use, went something like this:
    Code:
    PF = {};
    local PV = {};
    
    PF.Connect = function(Name)
    	local PlayerID = 0;
    	
    	for Player2ID, PlayerTable in pairs(PV) do
    		if(not(PlayerTable.Connected)) then
    			PlayerID == Player2ID;
    			break;
    		else
    			if(PlayerTable.Name == Name) then
    				return Player2ID;
    			end
    		end
    	end
    	
    	if(PlayerID == 0) then
    		PlayerID = (#PV + 1);
    	end
    	
    	local GameID = nil;
    	
    	GameID = GetPlayerByName(Name); -- Eluna Function:: Returns nil when the player is changing maps.
    	
    	if(GameID == nil) then -- This will bug if the player is changing maps.
    		print("ConnectPlayer error! The player specified was not found");
    		return 0;
    	end
    	-- Other Eluna functions to get player information.
    	
    	if(PV[PlayerID] == nil) then
    		PV[PlayerID] = {};
    	end
    
    	PV[PlayerID].Connected = true;	
    	PV[PlayerID].GameID = GameID;
    	PV[PlayerID].Name = Name;
    	-- Other table variables, such as MaxHealth, Gold, etc.
    	
    	if(type(PlayerConnected) == "function") then
    		PlayerConnected(PlayerID);
    	end
    	return PlayerID;
    end
    
    PF.Disconnect = function(PlayerID, Kick)
    	if(PV[PlayerID] == nil or not(PV[PlayerID].Connected)) then
    		return false;
    	end
    	
    	if(Kick == nil or Kick) then
    		Kick(PV[PlayerID].GameID);
    	end
    	
    	if(type(PlayerDisconnected) == "function") then
    		PlayerDisconnected(PlayerID);
    	end
    	
    	PV[PlayerID] = {}; -- Set the entire table to nil.
    	PV[PlayerID].Connected = false; -- So PV.Connect can reuse the table.
    	return true;
    end
    I did this just now in 5minutes, don't mimic it's untested.

    Now, I'm kinda sortof doing the same thing but with C++, using Eluna for TrinityCore as a base.
    Last edited by SkittlesAreFalling; 01-26-2014 at 04:04 AM.
    Achievements:
    1 year registered Member Level X

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •