• Content Count

  • Joined

  • Last visited

  • Days Won


MrTasty last won the day on August 24

MrTasty had the most liked content!

Community Reputation

111 Excellent

About MrTasty

  • Rank
    Road Dawg


  • Location
    United Kingdom
  • Occupation
    Scripting Guru

Recent Profile Visitors

2,989 profile views
  1. local rnd = math.random(1, #cells[dim]) "attempt to get length of field '?' (a nil value)" means you've tried to get the length (# operator) of a nil value (cells[dim]), which means there is no value in the cells table with the index corresponding to the value of dim. Your if-check sets the value of dim to the value of defaultCells, 174, but cells[174] also doesn't exist. Only cells[10] exists. cells = { --[Int ID] = {{Cellák pos}} [10] = {215.36199951172, 108.91190338135, 1000.9522705078, 180}, [10] = {219.84390258789, 108.5353012085, 1000.9569702148, 180}, [10] = {223.37170410156, 107.92459869385, 1000.8811035156, 180}, [10] = {227.17649841309, 107.85209655762, 1000.6198120117, 180}, } By the way, you can't have multiple values under one index. The table above, after construction, is equivalent to cells = { [10] = {227.17649841309, 107.85209655762, 1000.6198120117, 180} } that is, every line starting with [10] = overrides the previous line starting with [10] = You probably wanted something like cells = { --[Int ID] = { [Dim ID] = { {Cellák pos}, {Cellák pos}, ... }, } [10] = { -- in interior 10 [174] = { -- in dimension 174 {215.36199951172, 108.91190338135, 1000.9522705078, 180}, -- first cell in int 10 dim 174 {219.84390258789, 108.5353012085, 1000.9569702148, 180}, -- second cell in int 10 dim 174 {223.37170410156, 107.92459869385, 1000.8811035156, 180}, -- third cell in int 10 dim 174 {227.17649841309, 107.85209655762, 1000.6198120117, 180}, -- fourth cell in int 10 dim 174 }, }, } -- ... local int = getElementInterior(player) if int == 0 or not cells[int] then -- if interior is 0 or there are no cells in the interior int = defaultInt -- set int to default (10) end local dim = getElementDimension(player) if not cells[int][dim] then -- if there are no cells in that interior and dimension dim = defaultCells -- set dimension to default (174) end local rnd = math.random(1, #cells[int][dim]) -- pick a random cell from 1 to however many there are for that interior and dimension local randomCell = cells[int][dim][rnd] -- index the cells table for the random cell -- ...
  2. MrTasty

    Help with SQL

    The client itself doesn't and shouldn't have access to the SQL database. If you need to run an SQL command upon the request of a client, do so by having the client triggerServerEvent and the server executeSQLQuery. Do not let the client choose what query to run. The server should always build the query and look out for and prevent possibilities of SQL injection (e.g. a client asking the server to remove the whole database - big no-no).
  3. rank = ind arank = tonumber(rank)+1 areach = levels[arank] -- arank is not guarenteed to be a valid index, thereby returning a nil value oreach = val.reach -- and thus this line would fail by attempting to index such nil value You need to check if whether the player is at the last level before trying to calculate areach. Also, you should definitely look into using local variables more, it's inefficient to use global variables. function getRankForEXP(plr,exp) local newrank = false local currank = tonumber(getElementData(plr,"Level")) or 0 local curreach = tonumber(getElementData(plr,"LevelXP")) or 100 local curoreach = tonumber(getElementData(plr,"oLevelXPReach")) or 0 local rank, areach, oreach -- declare these local in this scope, so when they're written to they don't use _G but rather upvalues for ind,val in ipairs(levels) do if ind > currank and val.reach <= exp then rank = ind areach = levels[ind+1] -- optimised this a little if areach then areach = areach.reach else -- TODO: final level, no areach end oreach = val.reach newrank = true end end if newrank then return rank,areach,oreach end return currank, curreach, curoreach end
  4. The snippet you've posted seems to be so carelessly copied and pasted it doesn't look valid. I can take a couple guesses such as 'or' actually being 'for', etc. but it would be better if you posted a more complete example, including the definition of the levels table, and the definition of getRankForEXP (if it's not the code right above, stripped of it's function definition line)
  5. You should never create a timer on every frame like you're doing right now. It will create 60 timers in a second, 300 in 5 seconds (meaning you'll have up to 30 timers running at any one time just from this code). Looking at the code, you don't even need the timer if you're using interpolateBetween, so remove lines 25 through 28. The pos variable can and should be local to make lookup and reading faster on Lua. Your problem appears to be in duration math. local endTime = start + 1000 local duration = endTime - start -- should just be local duration = 1000 Second problem is that you don't seem to move the text at all, just the rectangle, but I hope you can figure that one out on your own.
  6. MrTasty

    Fixed dx image

    dxDrawImage3D useful function (based upon dxDrawMaterialLine3D) is what you're looking for.
  7. MrTasty

    table problem

    local button = {} function createMenu() button[1] = ... button[2] = ... -- either add handlers yourself addEventHandler("onClientGUIClick", button[1], click, false) addEventHandler("onClientGUIClick", button[2], click, false) -- if you add a false after the function name (getPropagated parameter), the function won't trigger for the elements parents (e.g. the parent gui window) -- or use a loop here for i = 1, #button do -- start at i = 1 and perform the body of the block and increment i until i = #button (number of elements in button table) addEventHandler("onClientGUIClick", button[i], click, false) end end function click() -- beacuse getPropagated is set to false on all handlers, you don't need to test whether this is a valid source (that is, source will never be your guiwindow), all you need to do is branch depending on which button it is if source == button[1] then ... elseif source == button[2] then ... end end Would be a much better way of doing this, imo. I'm not sure but you may need to put the click function above the function with addEventHandlers.
  8. If you nullify the unix_socket variable (nil or false), it will use the 'host' variable. Put the IP you want in host. mainDB = dbConnect("mysql", (unix_socket and "unix_socket="..unix_socket or "host="";dbname="..dbname, user, password) local unix_socket = nil local host = "" -- your ip here mainDB = dbConnect("mysql", (unix_socket and ("unix_socket="..unix_socket) or ("host="";dbname="..dbname, user, password)
  9. To check if JSON is valid itself, all you need to do is check whether the result of fromJSON is not a nil. If you want to make sure the actual values within that JSON are correct and within bounds, you'll simply need to make your own function which tests every part of the JSON, makes sure the required keys are present or assign default values to them, make sure the values are within the bounds, etc. function checkJson(text) local theTable = fromJSON(text) if not theTable then return false end -- it's not valid JSON if not theTable.some_required_key then return false end -- doesn't have the required key-value pair if theTable.some_required_key > maximum_allowed_value then return false end -- actual value exceeds allowed value if theTable.some_other_key and theTable.some_other_key > maximum_allowed_value then theTable.some_optional_key = maximum_allowed_value end -- update the value to be within the bounds if it would otherwise be outside it -- etc return theTable -- return the updated table if changes were made end function loadMyJson() local f = fileOpen("settings.json") local json = fileRead(f, fileGetSize(f)) fileClose(f) local settings = checkJson(json) if not settings then outputChatBox("Could not load settings.json. Loading defaults...") settings = g_DefaultSettings end -- etc end If you simply don't want people to change the file manually at all, you could encodeString with TEA, or encodeString with TEA a SHA256 hash of it and store it alongside, and check if the hashes match. The person won't be able to generate a valid hash if they don't know how the TEA secret. This is in effect similar to what a HMAC does but I don't think MTA has HMAC functions built in. Naturally, if you choose the latter, the safest bet would be to have the server send the tea secret via an event rather than having that secret hardcoded in the script file. It would still be possible to lift it out of memory either way, but at least if it's on the server it can't be somehow extracted out of the file offline, through decrypting, decompiling or whatever.
  10. MrTasty


    Well, I'm not sure if there's any significant performance loss, but definitely no timer is better than with timer. Timers, however little performance impact they have, do add up, and too many timers may contribute to slowdowns. A better approach would be like this local x,y = guiGetScreenSize() local duration = 8000 -- duration of full alpha, in miliseconds local fadeTime = 700 -- duration of fading from 255 to 0 local startTick = getTickCount() -- do this whenever you want the "LEVEL UP!" text to appear for duration + fadeTime function drawText() local ticksPassed = getTickCount() - startTick -- compare the current tick count to the starting tick count to calculate the number of ticks passed local alpha = 255 -- set the alpha to full 255 at the beginning (this'll be used as the alpha value until ticksPassed is greater than duration) if ticksPassed > duration then -- if number of ticks passed exceeds threshold, start reducing alpha alpha = math.max(0, alpha - ((ticksPassed - duration) / fadeTime)) -- subtract from 255 a progressively larger number as the ticks past duration approach duration + fadeTime, clamping at 0 so it doesn't go into negatives end if alpha > 0 then -- if alpha is greater than 0, queue the text draw (a little optimisation as there's no need to queue it if it's completely invisible, while at a cost of branching evaluation, it's still faster than calling hardcoded functions) dxDrawText("LEVEL UP!", x/2, y/2, x/2, y/2, tocolor(255,255,255,alpha), 2, "bankgothic", "center", "center") end end addEventHandler("onClientRender", root, drawText) -- an additional improvement would be to only bind the event to a handler when necessary, unbinding it once the text doesn't need to appear. After all, this function will be triggered every frame simply for it to make some math and draw no text if getTickCount() - startTick > duration + fadeTime This is a lot more maintainable, and uses only one function to do one thing. Your code uses to functions to do what one could do. This code only has one dxDrawText call, meaning if you want to change some parameters, you only need to do it in one place, not two.
  11. MrTasty


    The errors appear because you're constantly attempting to bind the onClientRender event to drawText or fadeText when it's already bound to it. Specifically, line 5 creates a new timer every single frame, and after 5 seconds all those timers elapse and attempt to bind the event to fadeText, at the same rate (e.g. 30 times a second if you run on 30 FPS and hence create 30 timers per second). The easiest way to solve this would be an isTimer check before creating a new timer to make sure it doesn't exist yet. However, the whole code design is messy and not ideal. You should think about another way of writing that code, from scratch. Using only one event handler.
  12. This is malformed XML (line 9). You forgot a closing angled bracket > <config src='skins.xml' type=''></config>
  13. Both posts above mix server-only functions with client-only functions. Smh. --server function refreshListAdmin() local onlineAdmins = {} for i, v in ipairs(getElementsByType("player")) do if hasObjectPermissionTo(v, "function.setPlayerMuted", true) then table.insert(onlineAdmins, getPlayerName(v)) end end triggerClientEvent(client, "receiveListAdmin", client, onlineAdmins) end addEvent("refreshListAdmin", true) addEventHandler("refreshListAdmin", root, refreshListAdmin) --client WINDOW_WITH_GRIDLIST = guiCreateWindow(...) GRIDLIST = guiCreateGridList(...) guiGridListAddColumn(GRIDLIST, "Admin Name", 0.85) function showListAdmin() if guiGetVisible(WINDOW_WITH_GRIDLIST) then guiSetVisible(WINDOW_WITH_GRIDLIST, false) -- hide the window if it's already visible return end guiGridListClear(GRIDLIST) guiGridListAddRow(GRIDLIST, "Loading...") -- add a row that says "Loading..." while waiting for server to return the admins list guiSetVisible(WINDOW_WITH_GRIDLIST, true) triggerServerEvent("refreshListAdmin", localPlayer) end addCommandHandler("admins", showListAdmin) function loadListAdmin(admins) if not guiGetVisible(WINDOW_WITH_GRIDLIST) then return -- don't continue if the user already closed the window end guiGridListClear(GRIDLIST) -- clear out the "Loading..." row if #admins > 0 then for k, v in pairs(admins) do guiGridListAddRow(GRIDLIST, v) end else guiGridListAddRow(GRIDLIST, "No admins online.") end end addEvent("receiveListAdmin", true) addEventHandler("receiveListAdmin", root, loadListAdmin) You'll need to incorporate this code into your existing code. Specifically, plug in the proper variables for your GUI window and gridlist.