MaddDogg Posted December 22, 2009 Share Posted December 22, 2009 Hi! I just switched from SA:MP to MTA, because SA:MP p*ssed me off more and more, and so I just started learning LUA from yesterday on. Luckily, I can connect several behaviours and syntaxes from LUA with Pascal and ActionScript, in which I'm quite experienced. But still I got some (noob) questions, for which I couldn't figure out an answer yet. It would be very nice, if you could help me with the following: How to call a function from a lua script inside the same resource? For example a function in the clientside script, which I need to call by the serverside script. I already tried with something like this.. call(getResourceFromName("test"), "setSomeVar", var, "string") ..but it didn't work. Also just calling the function like it would be inside the same script didn't work. Or do I always have to use event triggering? And yes, I added an export node to the meta.xml to export my function. Also, how call an outside function then, which returns values? Is there a resource or something like that that provides a graphical interface for the inbuilt SQL server? Or is there a way to use an external server, so that I could use phpMyAdmin? It's not that I'm too unskilled in MySQL, but it would spare me the scripting of an ingame GUI for it. You don't have to, if you think this question is too nooby, but can you explain to me the meaning of 'in ipairs' and 'in pairs' as used in for loops? What is the LUA equivalent of "new const IM_A_CONST[] = {100, 200, 300};"? Thanks, if you could help me with all this, I would really appreciate it. And respect to the MTA team, I'm just amazed by the mass of manipulable things ingame =D> Link to comment
robhol Posted December 22, 2009 Share Posted December 22, 2009 Aww.. this topic touches my heart. :') For the export issue, you will have access to another, neater form. Suppose you have declared and exported this function in a resource: function asd(foo, bar) end Assuming you've exported it correctly (and restarted the resource in which it's declared) you can call it from any other resource with exports.myResource:asd( "value of foo here" , "this would be the value of bar" ) The database file is called server/mods/deathmatch/registry.db and is standard SQLite. You can open it with any SQLite editor, for example this. As for your third question, it's pretty simple. I don't know of any Lua equivalent for "const", so.. just don't change the variable. IM_A_CONST = {100, 200, 300} If you haven't seen it already, I think you might find my scripting introduction helpful. http://robhol.net/guide/basics Edit: oh, I misunderstood. For intra-resource function calls, it really couldn't be simpler. Make sure your functions are not local, and you will be able to trigger them as if they were declared in the same file. Just call the function normally, you don't need call(), exports or events for that. If you need server-client communications, you must use events, though. Link to comment
Mr.Hankey Posted December 22, 2009 Share Posted December 22, 2009 You don't have to, if you think this question is too nooby, but can you explain to me the meaning of 'in ipairs' and 'in pairs' as used in for loops? This page at the Lua wiki should be a good source to learn about tables and arrays and of course it will also answer your questions What is the LUA equivalent of "new const IM_A_CONST[] = {100, 200, 300};"? Tbh i can't think of a situation where you would need a constant in Lua. Just create a variable that holds an array like this: simple_array = {100, 200, 300} Making a variable constant is pretty complicated though and would probably just work with table/array type vars. Is there a resource or something like that that provides a graphical interface for the inbuilt SQL server? Or is there a way to use an external server, so that I could use phpMyAdmin?It's not that I'm too unskilled in MySQL, but it would spare me the scripting of an ingame GUI for it. https://community.multitheftauto.com/index.php?p= ... ils&id=495 Thats probably what you're looking for but you can also just open up the registry.db file which is located in server\mods\deathmatch with any SQLite browser. Also note the the inbuilt sql server is using SQlite but you can also get a well working MySQL module (mta equivalent to plugins) at our wiki. Link to comment
subenji99 Posted December 22, 2009 Share Posted December 22, 2009 You both misunderstood his question about call. Yes, generally, between server and client lua files in the same resource you use events, and you will need a secondary event to get a returned value. As a side note however, if you don't actually need a return value and just want to trigger a function on the "other side", look at these 2 code examples: https://wiki.multitheftauto.com/wiki/CallClientFunction https://wiki.multitheftauto.com/wiki/CallServerFunction Lua doesn't support constants by design, but usually, you don't need to protect a variable from changes either, so a variable will suffice. If you do need to protect a variable from changes, an example is here: http://blog.stranadurakov.com/2009/05/05/lua-constants/ (note that is just standard Lua, not MTA related. Also it deals with metatables, which you shouldn't attempt while you are unfamiliar with standard tables.) The difference between pairs and ipairs is usually just a matter of processing cost in the majority of cases. When you assign values to a table, if you don't provide a key, Lua assigns an integer key, starting at 1 and incrementing for every new value added. ipairs iterates over the table in order, starting at key 1, and stopping when it finds a nil key. (even if there are higher-numbered keys later on) Whereas pairs iterates the whole table, regardless of what keys are there, in whatever order it finds them (which may or may not be numeric/alphabetical/order they were added in) So if the order you iterate the table matters and you used integer keys, use ipairs, otherwise, pairs will suffice and costs less processing time. Link to comment
MaddDogg Posted December 22, 2009 Author Share Posted December 22, 2009 First of all thanks for the answers It really cleared up some things for me. So, when I want to call a custom function, which lies in a second serverside script of the resource, I just call it normally by for example.. local value = getSomePlayerValue(player) But if this function is in a clientside script and I want to call it out of the serverside script, I have to use events? But how do I get the value that is returned? Because of course I can't fetch this from triggerClientEvent. And another question: I have an account system and fetch the account data of an user by a query. What would be the best way to store the data? I want to store the data in a big table called pData, but how do I do that exactly? The players don't have fixed server slot IDs, so I can't just put them into pData[5] for player in slot 5. And I also want to clear the data again, when the player leaves. Thank you guys for your help! Link to comment
subenji99 Posted December 23, 2009 Share Posted December 23, 2009 Correct for your first question - when both files are serverside or both are clientside in the same resource, you just call your function just like any other (as long as you didn't declare the function local that is) For a returned variable, you'll need to triggerServerEvent with another event for the reply. MTA uses elements rather than ID's, so you would assign your table as function myFunction(thePlayer, value) if pData == nil then pData = {} end pData[thePlayer] = value end --example myFunction(getPlayerFromName("Bob"), "someString") and to clear: function cleanUp() pData[source] = nil --look up hidden variables in the events wiki page end addEventHandler(onPlayerQuit, getRootElement(), cleanUp) Note that the value can be anything - string, number, even another table. Link to comment
eAi Posted December 23, 2009 Share Posted December 23, 2009 Welcome! You can't directly call client or server functions from the other side - this is mainly for security reasons - you should use events. You can't get return values because the function may take a substantial amount of time to return, and if you waited it'd pause all the other scripts on the server/client while that happened. Events are pretty easy to use though. I'd advise against using the callServerFunction/callClientFunction functions on the wiki at least until you've got a good understanding of scripting. They may seem like they make your life easier, but they won't help you learn how MTA scripts should really be written, and they potentially open your script up to hackers. I'd also suggest that using the built-in SQLite server is better than using the MySQL module, it should be able to cope with anything except the most extreme amounts of data, and should be easier to get to grips with. Link to comment
MaddDogg Posted December 23, 2009 Author Share Posted December 23, 2009 Thanks for the answers! So, would the following be correct? addEventHandler("onCorrectLoginData", getRootElement(), function (player, loginname) pData[player] = executeSQLQuery("SELECT level, money, points FROM playerdata WHERE name = '" .. loginname .. "'") outputChatBox("You're now logged in!", player) end ) The result should be a table which can be used like this: ... if pData[player]["level"] > 1 then outputChatBox("You are an admin!", player) end ... Thank you again! Link to comment
subenji99 Posted December 23, 2009 Share Posted December 23, 2009 Close. ReturnsReturns a table with the result of the query if it was a SELECT query, or false if otherwise. In case of a SELECT query the result table may be empty (if there are no result rows). The table is of the form: { { colname1=value1, colname2=value2, ... }, { colname1=value3, colname2=value4, ... }, ... } So your returned table would look like: pData[player][1]["level"] == 1 --for the first returned row of values pData[player][2]["level"] == 1 --if the query finds a second matching row ... Link to comment
MaddDogg Posted December 23, 2009 Author Share Posted December 23, 2009 Okay, so the following is then giving me my wanted result?: pData = { "name" = {}, "password" = {}, "level" = {}, "points" = {} } pDataValueTypes = {"name", "password", "level", "points"} function getAccountValues(player, loginname) local tmpvalues = executeSQLQuery("SELECT name, password, level, points FROM playerdata WHERE name = '" .. loginname .. "'") for i=1, types in ipairs(pDataValueTypes) do pData[player][types] = tmpvalues[1][types] end outputChatBox("#FF0000Data successfully read!", player, 0, 0, 0, true) end Everything correct here? The table correctly initialized? Is there perhaps an example or published account system, at which I can take a look to see how it's done there? Or any tutorial? Link to comment
eAi Posted December 23, 2009 Share Posted December 23, 2009 Why not try out the code? https://community.multitheftauto.com/index.html?p=resources has some login resources. Link to comment
MaddDogg Posted December 23, 2009 Author Share Posted December 23, 2009 Alright, thanks for the help! I now did it with the MySQL module, because later on I want to be able to modify the data through a web browser UCP. Here's my example code, if it helps anyone to create his account system: pData = {} pDataValueTypes = {"name", "password", "level", "points"} function CheckMySQLConnection() if mysqlCon == nil or mysql_ping(mysqlCon) == false then outputServerLog("Establishing MYSQL connection..") mysqlCon = mysql_connect("localhost", "CHANGEME", "CHANGEME", "CHANGEME", 3306, "/var/run/mysqld/mysqld.sock") if mysqlCon == nil then outputServerLog("ERROR: Couldn't connect to database!") return false end end return true end function getAccountValues(player, cmd, loginname, password) if CheckMySQLConnection() == false then return outputChatBox("#FF0000Wasn't able to connect to database!", player, 0, 0, 0, true) end if pData[player] ~= nil then -- if player table already exists --> player is already logged in return outputChatBox("#FF0000You're already logged in!", player, 0, 0, 0, true) end if loginname == nil or password == nil then -- if parameters are not given return outputChatBox("#55FF55Use this syntax: /testlogin [NAME] [PASSWORD]", player, 0, 0, 0, true) end tmpResult = mysql_query(mysqlCon, "SELECT name, password, level, points FROM pdata WHERE name = '" .. loginname .."'") -- getting account data if(mysql_num_rows(tmpResult) == 0) then -- if name is not existent return outputChatBox("#FF0000This name is not registered!", player, 0, 0, 0, true) end -- fetching the data local tmpValues = mysql_fetch_assoc(tmpResult) mysql_free_result(tmpResult) if password ~= tmpValues["password"] then -- if given password is not matching saved one return outputChatBox("#FF0000The password is not correct!", player, 0, 0, 0, true) end pData[player] = {} -- initialize player table -- fill player table with fetched data for i,types in pairs(pDataValueTypes) do pData[player][types] = tmpValues[types] end outputChatBox("#FF0000You were successfully logged in! Your points: #00FFFF" .. pData[player]["points"], player, 0, 0, 0, true) -- success message end addCommandHandler("testlogin", getAccountValues, false, false) Link to comment
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now