• Content Count

  • Joined

  • Last visited

Community Reputation

97 Popular


About MrTasty

  • Rank
    Road Dawg


  • Location
    United Kingdom
  • Occupation
    Scripting Guru

Recent Profile Visitors

2,143 profile views
  1. MrTasty

    Load Browser In Game

    This is because is on the default whitelist. Clients must give permission to open addresses, so you need to requestBrowserDomains (as you have correctly done). However, you should use the callback argument in that function (or "onClientBrowserWhitelistChange" event) to know that the user gave permission and only then attempt to load the page. Trying to load it before permission has been granted won't load it.
  2. MrTasty

    Local voice chat

    I've just released a 3D proximity based voice chat resource which I had sitting on my disk being unused. Initially wanted to wait until MTA's voice system is updated to Opus and voice volumes are fixed as they're extremely quiet still.
  3. MrTasty

    need help core Server

    This is line 150: elseif ( #exports.DENmysql:query( "SELECT * FROM accounts WHERE serial = ?", getPlayerSerial( source ) ) >= 2 ) then The problem is simple. The query exports.DENmysql:query( "SELECT * FROM accounts WHERE serial = ?", getPlayerSerial(source)) failed and returned a boolean instead of a table, thus the # (length operator) raised an error. P.S. Use code blocks next time. It's very unhelpful to share code without line numbers. Also, you should provide more info as well. Saying "need help" isn't going to attract people willing to help. Also, share only relevant lines of the code, please? It's annoying to have to scroll through 400 lines of code when we're just interested in 1 line.
  4. MrTasty

    CEF and Fontawesome

    Yeah. If the browser is local, it's local only, and cannot communicate with the internet on it's own. This is because local browsers enable extended JavaScript functionality, namely, calling events etc. in MTA. If this was allowed, a malicious website would be able call events and exploit client-sided code, perhaps even the server-side integration of it. Browsers that can communicate with the internet, have that functionality disabled for that reason. Technically you could get around this by loading the contents through fetchRemote and injecting that into a local browser, but as mentioned above, this would be very difficult to get images and anything else that loads in a separate request unless you parse the fetch's return as a browser would and make those additional requests through fetchRemote too. This would likely also be a lot slower than a non-local browser.
  5. MrTasty

    Elevator problem with code

    A lot of issues in the serverside code. VehicleHit is a function defined within a function, which, while legal, doesn't make sense unless you're making a local function. VehicleHit is called by a timer started outside the qm function, meaning that qm must be called (via hitting a marker) within 1000 ms of starting the serverside code. Additionally, the timer does not send over any special parameters but your VehicleHit function expects a hitElement parameter and checks that it's a player element. You're sending a nil. qd event is only declared but never actually used. qd2 and qd4 aren't even declared on the serverside.
  6. addEventHandler("onClientConsole", localPlayer, function (text) local args = split(text, " ") -- split the text by spaces local command = table.remove(args, 1) -- remove the first word from the args table and put it into the "command" variable outputConsole(command) end ) This should work, but I haven't tested it. This is clientside code by the way. You can use onConsole on the server but I believe that won't trigger for commands handled by a command handler.
  7. MrTasty

    How to set AK-47 weapon properties to TEC-9?

    Then you probably just want to do setWeaponProperty(32, "pro", "flags", 0x000800) -- toggle 'can use 2x guns at same time' flag -- or setWeaponProperty(32, "pro", "flag_type_dual", false) -- turn off flag_type_dual (I've never used this method, can't attest as to whether it works)
  8. MrTasty

    How to set AK-47 weapon properties to TEC-9?

    setWeaponProperty(30, "poor", "flags", 0x000002) -- toggle 'only needs arm to aim' setWeaponProperty(30, "poor", "flags", 0x000800) -- toggle 'can use 2x guns at same time' -- etc. You need to use the numbers from to toggle them between on and off. If you want to know if it's on, use function isWeaponFlagSet(weapon, skill, flagBit) return bitAnd(getWeaponProperty(weapon, skill, "flags"), flagBit) ~= 0 -- collects the current weapon bitflags, filters them using a bitwise And operation to only retain bits of flagBit, and checks if it's not 0 end or if you prefer, you can use this function to explicitly enable or disable rather than switch flipping current state -- Set or clear an individual weapon flag bit function setWeaponPropertyFlag( weapon, skill, flagBit, bSet ) local bIsSet = bitAnd( getWeaponProperty(weapon, skill, "flags"), flagBit ) ~= 0 if bIsSet ~= bSet then setWeaponProperty(weapon, skill, "flags", flagBit) end end -- from setWeaponProperty wiki example
  9. MrTasty


    I believe coroutines cannot be sent over via callback arguments, so you'll have to store the coroutine reference in a table t at some index i and then send i for callback, and resume t[ i ] in the callback function. gDatabaseConnection = --[[ this would be your database connection here ]] local coroutines = {} -- coroutines will be stored here function gCallbackFn(qh, id) -- this is called when dbQuery thread returns indicating the query is ready to poll local result = dbPoll(qh, 0) -- since it's ready to poll, timeout is irrelevant and can be 0 coroutine.resume(coroutines[id], result) -- resume the coroutine with query result data end function handleQuery(queryStr, ...) if not coroutine.running() then error("handleQuery can only be called from within a coroutine.", 2) end local id = table.maxn(coroutines) + 1 -- calculate next unused table index (not sure whether # or maxn is better here) coroutines[id] = coroutine.running() -- store this coroutine in that table index dbQuery(gCallbackFn, {id}, gDatabaseConnection, queryStr, ...) -- this splits into two threads, first one continutes over to coroutine.yield function, other is internal to MTA and handles the query, and continues from gCallbackFn afterwards. local qData = coroutine.yield() -- since we want to wait for the query thread to return, we need to yield/pause this coroutine execution, and let gCallbackFn resume it when the query thread returns return qData -- return the query result data end -- now call handleQuery at some arbitrary point, here, after 1000ms (1s) c = coroutine.wrap( -- coroutine.wrap creates a coroutine wrapped in a function that, when called, will start/resume the coroutine function() local result = handleQuery("SELECT * sometable WHERE id = ?", 2) iprint(result) end ) setTimer(c, 1000, 1) -- call the function-wrapped coroutine after 1000ms I've only tested this using timers instead of dbQuery but it should work. N.B. this handleQuery function must be called from within a coroutine. If you're calling this when an event happens, you can override addEventHandler to automatically wrap handlers into coroutines too. local addEventHandler_ = addEventHandler function addEventHandler(eventName, attachedTo, handlerFn, propagation, priority) return addEventHandler_(eventName, attachedTo, coroutine.wrap(handlerFn), propagation, priority) end
  10. MrTasty

    Client script Cache, fileDelete(), luac

    Using cache="false" is definitely safer, as they cannot be downloaded directly via http://serverip:serverport/resourcename/path/to/file.lua (you'll get "This script is not client cacheable" return message), and the only way to hijack the source code would be via a hacked client or by wiretapping the connection with Wireshark or something and somehow breaking the encryption on it. On the other hand, without blocking caching, users can technically download the file (provided they know the path and its filename) without executing it, thus the file wouldn't be deleted via fileDelete. However, this also means if you're using a fast download external server to serve files, these files aren't cached or served by that external server, and instead, served directly from the game server. External servers like nginx can provide good compression to downloaded data, while MTA internal HTTP download server is very primitive and does not support compression or multiple client connections[source]. Now to answer the other questions: - Client scripts don't unload from memory except when restarting a resource or reconnecting to the server, in which case they're requested from the server again and downloaded (and saved if caching is enabled) and executed. - If you're using cache="false" there's no need to use fileDelete, and indeed it could risk revealing file paths unless you also check fileExists before, as errors in clientscript.log may reveal paths if the file is attempted to be deleted but does not exist.
  11. MrTasty

    Team Blip Color

    function setBlipsToTeamColor() for _,player in ipairs(getElementsByType("player")) do for _,blip in ipairs(getElementsByType("blip")) do local team = getPlayerTeam(player) if (team and getElementAttachedTo(blip) == player) then -- only if this blip is attached to `player` local r,g,b = getTeamColor(team) setPlayerNametagColor(player,r,g,b) setBlipColor(blip,r,g,b,255) end end end end setTimer(setBlipsToTeamColor,500,0) Also I've renamed the function since you should avoid naming your function with the same name as existing hardcoded functions (namely, setTeamColor) as that makes them inaccessible.
  12. MrTasty

    "Permanent" ID System

    If you want to have permanent IDs, it's best to simply assign ID numbers to accounts — and then insert and remove the player from the playerid table on login (and on resource start), logout and quit.
  13. MrTasty

    assalt-bank- help amount of four or more players to rob

    Use code blocks next time, please. And give some more details at what you mean to achieve. The title doesn't go into enough detail to get help - what should qualify four players? Four players online? Four players in proximity? Four players of a specific team? Four players of a specific team within proximity? And then, what should happen when there isn't four players? Should it continue if it started with four, or should it end abruptly? Et cetera. A lot of unanswered questions. The following code is reformatted to be more readable and has whitespace corrections that were bugging me, but behaviour-wise, it is unchanged. function openMyGate(Jogador) if isElementWithinMarker(Jogador, markers[1]) then setTimer(function() outputChatBox(getPlayerName(Jogador).." #00ff88● #FFFFFFEsta tentando assaltar empresa da BRINKS agora", players, 255, 255, 255, true) moveObject(objetos[1], 2500, 847.20001220703, -1371.5999755859, 22.700000762939) setElementPosition(markers[1], 848.06927490234-0.2, -1374.2985839844, 3000) destroyElement(markers[1], root, false) setTimer(function() -- wait 4502ms ped1 = exports["slothBot"]:spawnBot(830.59997558594, -1372, 25, 268, 163, 0, 0, nil , 38, "guarding") -- guarda parede 1 setTimer(function() exports["slothbot"]:setBotAttackEnabled(ped1, true) end, 100, 1) setTimer( function () -- wait 200ms ped2 = exports["slothBot"]:spawnBot(830.59997558594, -1367, 25, 262, 164, 0, 0, nil , 31, "guarding")-- guarda parede 2 setTimer(function() exports["slothbot"]:setBotAttackEnabled(ped2, true) end, 100, 1) setTimer(function() -- wait 200ms ped3 = exports["slothBot"]:spawnBot(832.90002441406, -1364, 25, 180, 165, 0, 0, nil , 27, "guarding") -- guarda cliente 1 setTimer(function() exports["slothbot"]:setBotAttackEnabled(ped3, true) end, 100, 1) setTimer(function() -- wait 200ms ped4 = exports["slothBot"]:spawnBot(838, -1363.9000244141, 25, 180, 166, 0, 0, nil, 31, "guarding") -- guarda client 2 setTimer(function() exports["slothbot"]:setBotAttackEnabled(ped4, true) end, 250, 1) ped9 = exports["slothBot"]:spawnBot(843.29998779297, -1363.8000488281, 25, 186, 163, 0, 0, nil , 38, "guarding") -- guarda cliente 3 setTimer(function() exports["slothbot"]:setBotAttackEnabled(ped9, true) end, 100, 1) ped10 = exports["slothBot"]:spawnBot(830.40002441406, -1375, 25, 262, 164, 0, 0, nil, 27, "guarding") -- guarda 1 parede gerente setTimer(function() exports["slothbot"]:setBotAttackEnabled(ped10, true) end, 100, 1) ped11 = exports["slothBot"]:spawnBot(830.5, -1383, 25, 286, 165, 0, 0, nil, 31, "guarding") -- guarda 2 parede gerente setTimer(function() exports["slothbot"]:setBotAttackEnabled(ped11, true) end, 100, 1) end, 200, 1) end, 200, 1) end, 200, 1) end, 200, 1) end, 4502, 1) setPedRotation(Jogador, 87.794250488281) setElementPosition(Jogador, 847.806640625, -1374.2625732422, 22.59531211853) setPedAnimation(Jogador, "KISSING", "GF_CarSpot", 4500, false, false, false, false) setTimer(function() -- wait 1000ms triggerEvent(eventorestart, root) end, 1000, 1) end end addCommandHandler("arrombar", openMyGate)
  14. MrTasty

    [HELP] How to use .CMF files

    Firstly, I believe those aren't scripts, but rather models, images and whatnot - the filenames don't sound like script files. Secondly, it's probably a normal text file with either the raw data or encrypted (TEA perhaps) data. You can simply rename your models to use a different extention and they'll continue to work, while being more obscute to individuals lurking in downloaded resources cache. For more protection, you can store encrypted data in those files, and decrypt it on the go in a script. Dignum memoria, decrypting anything on a remote machine requires you to send a decryption key to it (via event or as a constant in a script file, or some other way), meaning that it will be possible for people to decrypt the data on their own, just a lot harder.
  15. MrTasty


    You could using string.match to ensure that from start to end, only letters (%a) and the underscore (_) symbol are allowed. E.g. return string.match(str, "^[%a_]+$") == str ^ matches beginning of string, [...] makes a set, %a includes all letters (uppercase and lowercase) into the set, _ adds underscore into the set, the + means it can match more than once, and $ matches the ending of the string. The function returns the matched string, which should be identical to the input string since we're matching from start to end. It returns a nil if nothing matched, that is, the input string wasn't from start to end only letters and underscores. It is very lenient and allows names even like Test__, or __john_sMiTh__, etc. No enforcement of where the underscore can be, how much can there be, and no enforcement of capitals. If you want it to be a little more rigid, for example, that the underscore must separate two different words made of letters, you could use the following return string.match(str, "^%a+_%a+$") == str and if you want to enforce capital letters at the beginning of these two words, return string.match(str, "^%u%l+_%u%l+$") == str -- must begin with capital letter and all subsequent must be lowercase return string.match(str, "^%u%a+_%u%a+$") == str -- must begin with capital letter and all subsequent can be either case