This is because youtube.com 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.

I've just released a 3D proximity based voice chat resource which I had sitting on my disk being unused. https://community.multitheftauto.com/index.php?p=resources&amp;s=details&amp;id=15958. Initially wanted to wait until MTA's voice system is updated to Opus and voice volumes are fixed as they're extremely quiet still.

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.

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.

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.

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.

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)

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 https://wiki.multitheftauto.com/wiki/Weapon_Flags 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

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

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.

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.

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.

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