Jump to content

Addlibs

Members
  • Posts

    1,060
  • Joined

  • Last visited

  • Days Won

    9

Everything posted by Addlibs

  1. What do you mean by "to JavaScript"? Where is it running? Are you talking about a js script on a website? If so, you can send and receive data to and from a Js script using HTTP requests to the relevant MTA server web interface (which uses HTTP Basic authentication over unencrypted HTTP). A JavaScript webserver (such as node.js) can do this too. Note, if you intend to connect to the HTTP web interface for your server, the best practice is to only do HTTP connections internally on the same network, and block the port for any external IPs - instead, route the external traffic through a gateway reverse proxy like nginx or Apache with HTTPs enabled. Otherwise you risk exposing privileged login credentials. MTA scripts can likewise send and receive data from JS websites using HTTP requests fetchRemote and callRemote. Data can be serialized using toJSON and receive them using fromJSON. This will not work for userdata types, including all elements. You should send names, identifiers or any other data you may need for processing any such element, as they will not be obtainable through JS, ref and unref functions exist and can allow you to send numerical references to the elements that can be de-referenced at another point in time, but in general, it is better to assign element IDs (setElementID and getElementID) to unique values and use those to send references to these elements. You can look up (de-reference) the element using getElementByID.
  2. No. Since "lctrl" and "mouse1" are not equal to each other, If key is "lctrl" then by definition it cannot be "mouse1", therefore this conditional always evaluates false, and cancelEvent is never called. If you want to block either lctrl or mouse1, keep the logical connective as or. addEventHandler("onClientKey", root, function(key, pressState) if key == "lctrl" or key == "mouse1" then -- block the use of either key cancelEvent(true) end end ) If you want to block both from being used at the same time, you instead have to use an internal state to remember whether the other key is pressed. For example, have a variable (bool) remember whether lctrl is currently pressed or not, and when key == "mouse1" you can test local lctrlIsPressed = -- assigned at previous invocation of onClientKey addEventHandler("onClientKey", root, function(key, pressState) if key == "lctrl" then lctrlIsPressed = pressState -- remember the pressState of lctrl elseif key == "mouse1" and pressState and lctrlIsPressed == true then -- only if mouse1 was now pressed and lctrl is already pressed cancelEvent(true) end end ) Note the example above is only half correct; it prevents the detection of pressing mouse1 while lctrl is pressed, but not the opposite; if one clicks and holds mouse1 and only then presses lctrl, this will not be prevented from detection by the game.
  3. The best way to sync fire is the same way you'd sync most things, and there are two options: the server creates fire and distributes information about it to clients, or clients create fire and inform the server, which then broadcasts the information to the other clients. The second option is basically a slightly expanded version of the first, so I'll outline how the first could work: Server runs a resource responsible for synchronizing fires between players. This resource has an exported function or custom event Security point: Don't allow remote trigger for custom events unless necessary, keep it server-triggered to prevent untrusted clients from being able to triggerServerEvent and spam the server with unauthorized fire; or, allow remote trigger but add logic to prevent unruly clients from being able to spawn fire where ever they want. When the exported function is called, or the custom event triggered, the server adds to its table of existing fires certain data about the fire, including position, interior, dimension, size, maximum duration and timestamp or tickcount when spawned, and triggers a client event for all players: onReceiveSyncedFireData (or whatever you choose to name it) and sends this data through Each client handles this event, and calls createFire on the client, along with timers to delete the fire when the current timestamp/tickcount exceeds the spawn timestamp/tickcount plus duration If a tickcount is used, it's best if the client at this point determines their tickcount offset from the server's, and uses its own tickcount plus/minus server offset for further calculations. A new client joining should request existing fires by triggering on the server an event onRequestSyncedFireData (or whatever you choose to name it) The server handles this event by triggering on the new client the previously mentioned receive event for each fire in the server's fires table. This client handles the receive events the exactly the same as if the fires were newly spawned. If your scripts move fires around, you may add events that allow clients to make updates, in a similar way to the above but information flowing from the client to the server, and then broadcast down to the other clients Security point: ideally check that only the element syncer is allowed to make changes to a fire, to prevent cheaters from tampering with fires they're nowhere near
  4. for theKey,peds in ipairs(ped) do if ped and isElement(peds) and isElementStreamedIn(peds) then local Zx,Zy,Zz = getElementPosition(peds) zedSound = playSound3D(zombiesounds[math.random(1,#zombiesounds)], Zx, Zy, Zz, false) zedSound variable is overwritten for every ped. This means you're creating new sounds but discarding the pointer/reference/element handle for the old one. The old one will thus remain playing and you have no way* to delete it. What you need instead is a table of sounds you've spawned, and when you call stopSound, you should call it once per sound element in that table. This should stop all the sounds you've created. Example: -- when creating spawnedSounds = {} for theKey,peds in ipairs(ped) do if ped and isElement(peds) and isElementStreamedIn(peds) then local Zx,Zy,Zz = getElementPosition(peds) zedSound = playSound3D(zombiesounds[math.random(1,#zombiesounds)], Zx, Zy, Zz, false) -- do whatever with zedSound spawnedSounds[ped] = zedSound -- store the zedSound into a table for later when calling stopSound -- when stopping for theKey,theSound in pairs(spawnedSounds) do -- go through each sound in the table (note the use of pairs is -- essential if the keys of the table are not numerical and sequential: in this case they are elements so pairs, is needed) -- (also NB: some performance comparisons point out pairs is faster than ipairs even with sequential numeric tables -- so you can just blindly always use pairs instead of ipairs without performance impact) stopSound(theSound) -- stop the sound that is stored as the value of a key=value pair in each row of the table end * You can still recover the pointer/reference/element handle though functions like getElementsByType for sound type but its going to be much harder to identify which one you want.
  5. Don't generate a new passwordHash on login. passwordHash should be used when registering, and only once; the result should be saved in the database. When logging in, take that hash (which includes a salt) and use passwordVerify of the input password against the saved hash. What this does internally is take the salt from the hash and similarly hash the input password, using the salt from the database hash, to come up with the exact same resultant hash if the provided plain text password is correct, or a different hash if it isn't (the result of the comparison of these hashes is the boolean return from passwordVerify). -- ... local passHashFromDB = result[1]["password"] local passVerified = passwordVerify(pass, passHashFromDB) if passVerified then print("Sikerült") else print("Nem Sikerült") end -- ... Also, don't trust the client to hash the password! The server should generate the hash in the server-side handling of the registration event: -- client side function Reg() local Empty = false if Data[2][1] == "" or Data[2][2] == "" then Empty = true LogAlert("EmptyRectangle") end if not Empty then if string.len(Data[2][1]) > 3 then if string.len(Data[2][2]) > 3 then if Data[2][2] == Data[2][3] then triggerServerEvent("attemptReg", resourceRoot, Data[2][1], Data[2][2]) else LogAlert("NotMatch") end else LogAlert("ToShortPass") end else LogAlert("ToShortUS") end end end -- server side addEvent("attemptReg", true) addEventHandler("attemptReg", resourceRoot, function(username, pass) local serial = getPlayerSerial(client) local dq = dbQuery(db, "SELECT * FROM accounts WHERE serial=?", serial) local result = dbPoll(dq, 250) if result and #result > 0 then outputChatBox("Felhasználó már létezik.", client) else if (not result) then -- abort function early to avoid inserting duplicate accounts!! outputDebugString("DATABASE LOOKUP FAILED: Aborting registration for " .. username .. "/" .. serial) return end dbExec(db, "INSERT INTO accounts (username, password, serial) VALUES (?, ?, ?)", username, passwordHash(pass, "bcrypt", {}), serial) outputChatBox("Sikerült", client) end end) I added an extra early-abort code, but this should not be used in production! Change this to use callbacks. Your existing code, if it failed to retrieve results within 250ms, would create duplicate accounts, because result would be nil (i.e. result not ready) so the "Felhasználó már létezik." output would not be run. Also, there is no checking for duplicate account names right now (only duplicate serial check); depending on how you set up your DB tables, this may still result in duplicate account names, or the DB will reject the insertion but the user will be told registration was successful. You may want to fix that.
  6. You should not be hashing the password on the client side: function Login() if Data[1][1] ~= "" and Data[1][2] ~= "" then local hashedPass = passwordHash(Data[1][2], "bcrypt",{}) -- this is wrong triggerServerEvent("attemptLogin", resourceRoot, Data[1][1], hashedPass) else LogAlert("EmptyRectangle") end end passwordHash generates a new salt, a salt that does not match with the salt saved on the database, meaning the results will never match. Indeed, you can try this yourself: local inputPassword = "somesecretpassword123" local hash1 = passwordHash(inputPassword, "bcrypt", {}) -- pretend this one is saved on the database some time ago local hash2 = passwordHash(inputPassword, "bcrypt", {}) -- pretend this one was just hashed now -- Note the following will never match: print(hash1) print(hash2) -- What you're doing: passwordVerify(hash2, hash1)) -- note, hash2 is not a password but the result of passwordHash given the input password -- What you should be doing: passwordVerify(inputPassword, hash1) -- this is how passwordVerify is supposed to be called Thus, you should have the following in the clientside: function Login() if Data[1][1] ~= "" and Data[1][2] ~= "" then triggerServerEvent("attemptLogin", resourceRoot, Data[1][1], Data[1][2]) else LogAlert("EmptyRectangle") end end And keep the current serverside the same.
  7. I'm not sure if this would work, but have you tried setPlayerVoiceBroadcastTo(thePlayer, nil) and setPlayerVoiceIgnoreFrom(thePlayer, root) in onPlayerJoin on the server side? This should be able to control the player's voice chat even if clientside scripts haven't downloaded, but I haven't tested this.
  8. You can set up a reverse proxy on your server machine if you have access to one. The idea is that you want to add a new HTTPS-enabled port that connects to nginx/Apache web server ("web server"), which in turn connects to the MTA server's HTTP server ("MTA server web portal") using non-secure HTTP (this is mostly fine if both programs run on the same machine, no external networks are used in between and the firewall is properly configured). Once that is set up, you can outright block the MTA server web portal access from all external IP addresses as well for added security. Now, you can point your iframes to this web server and it will display what the MTA server web portal displays. There are plenty of good reverse-proxy guides you can find for nginx/Apache. And while you're at it, you can also use this new nginx/Apache server as an external download server (see httpdownloadurl in mtaserver.conf) and enable gzip or zopfli (this one is slower but provides much more significant reduction in file size) compression (MTA clients support receiving compressed resources but the built-in MTA server does not compress resources before sending) to make the download size smaller for the players on your server.
  9. The only way this would work is if download priority was set higher for the resource with this code, otherwise this code won't execute until everything downloads and thus voice will be heard until download completes.
  10. addEventHandler("onClientMarkerHit", getJobMarker, hitMarkerWindow) -- should be changed to addEventHandler("onClientMarkerHit", getJobMarker, function(hitPlayer, matchingDimension) if (hitPlayer == localPlayer and matchingDimension) then hitMarkerWindow() end end ) This protects the window from being created when any element hits the marker, instead only triggering if the element that hits the marker is the client that is running this client-side script.
  11. Addlibs

    [HELP]

    You should carefully rethink your code design -- this may work for 1 or 2 car options, but as you add the 3rd, 4th, 5th etc, it gets really complicated unnecessarily. Instead, consider reusing existing code with iterations through a table and extracting parts of the code into their own functions. Here's a much more flexible version of your code, which does not rely on adding numbers to variable names, writing a cascade of if blocks and bind handlers within bind handlers, etc. local models = { 411, 478, 240 -- add as many models here as you want, and the code will work without any other changes } local displayVehicle, index -- declaring variables as local ahead of time allows to get the speed benefits of `local` while having variables accessible thoughout the current script file (not accessible in other scripts of the same resource though) local function updateDisplayVehicle() if (isElement(displayVehicle)) then -- to prevent generating errors when the vehicle doesn't exist destroyElement(displayVehicle) -- delete the existing vehicle if it exists end local vehModel = models[index] -- get the vehicle model corresponding to the current index displayVehicle = createVehicle(vehModel, 2154.45996, -1153.21362, 23.87550) -- spawns a vehicle with the correct vehicle model end local function tryChangeIndex(newIndex) if (models[newIndex]) then -- if the given index is valid (i.e. table `models` contains a value for that index) index = newIndex -- set current index to the given index updateDisplayVehicle() -- and update the currently spawned vehicle end end local function nextVehicle() tryChangeIndex(index + 1) end bindKey("arrow_r", "down", nextVehicle) local function previousVehicle() tryChangeIndex(index - 1) end bindKey("arrow_l", "down", previousVehicle) I've tried to provide comments that should help you understand what I'm doing in the code. I hope this helps.
  12. What nonsense is that? Root is an element sui generis that represents the top of the element tree, that is, all elements descend from it. Most, if not all, functions are recursive down the element tree (except where propagation is disabled), so setVehicleColor(root, ...) would, while returning false (because the function makes no sense on its principal element, root), set the color of every vehicle that descends from it, or in other words, every vehicle in the world. Then there's resource root, from which all elements spawned by one resource descend. These elements can be recalled with getRootElement and getResourceRootElement (and the pre-defined variables root and resourceRoot). You can read more about this on the MTA wiki page for the Element tree. Source is the element on which an event is triggered, an element that is the source of that event. Typically this will be the subject of the event name, for example, in the event onPlayerEnterVehicle, the player is the grammatical subject, and is therefore the source of the event. In an event handler -- that is, the function that is called when the event triggers (registered with addEventHandler) -- MTA defines a local variable called source that references said element. For example, in onPlayerEnterVehicle, the source variable references the player element of the player that entered a vehicle. The vehicle's element will be in the event handler's parameters. To look up what the source of an event is, and the parameters of each event, look up the wiki page for that event.
  13. These errors mean you're attempting to compare incomparable values, for example, a bool with a number. Is true > 1, < 1? It doesn't make sense. getElementData returns a 'false' value when there is no data stored under the given key -- all you have to do is prevent the comparison from happening if the returned value is not a number: local returnedValue = getElementData(someElement, "someKey") -- options: if (returnedValue) then if (returnedValue > 5) then -- valid end end if (returnedValue and returnedValue > 5) then -- valid end -- the following two will also work if the returned value is a string that contains arabic numbers in base-10, e.g. ("1000" (string) => 1000 (number)) if (tonumber(returnedValue) and tonumber(returnedValue) > 5) then -- valid end local returnedValue = tonumber(getElementData(someElement, "someKey")) if (returnedValue and returnedValue > 5) then -- valid end
  14. Please use the Portuguese section if you need help in Portuguese. Por favor utilize a secção portuguesa se precisar de ajuda em português. This post, translated with DeepL: sqlite.db "error loading data I think". <code> theoretically when entering the marker it should display the amount of fuel available in the station but it is displaying ( Could not get the amount of gasoline for the set ) any idea how to make it work correctly
  15. First issue: Lua is parsed and executed from top to bottom; by the time of the setTimer call, timerJail has not been declared or defined. You need to move the setTimer call after the timerJail function definition. Second issue: addEventHandler requires an element to bind onto (2nd argument) -- this means which element (and its children if propagation is enabled, which it is by default) should the event fire for -- and in your code it is an nil-value variable source, hence the error. source is defined within an event handler function, so getElementData(source, "jailLoc") is fine, source is the player that spawned. source outside that function, such as in the arguments passed to addEventHandler, it is undefined/nil. Change this to something like root (this is a pre-defined synonym for the value returned by getRootElement()).
  16. You can use Telegram's Bot API. First, if you haven't already, create a bot (by messaging BotFather on Telegram) and use callRemote or fetchRemote (only do this on the server side, for security reasons -- never send the bot's token to a client) to utilize the API. Bots cannot initiate conversations, but you can prompt users to start a conversation by displaying a URL (or preferably a QR code) that a user can scan and open the conversations. You can also use a parameter in the QR code that would allow the bot to correlate the Telegram user with the MTA player if you need that. You can also create a group chat or a channel and invite the bot into it, and use it to let users read and write to the ingame chat out-of-game.
  17. The commands you listed are incredibly easy to implement yourself (beginner level stuff, may be difficult if you have absolutely not idea what you're doing but once you learn the basics it should be easy), have you even tried? If so, show us your progress/attempts and what your specific issue is. We're not here to script for you.
  18. The callback function for bindKey callback function does not receive a player element. The client-side bindKey's player can only ever be the localPlayer, so you need to change this function signature: function burnoutTheTires(player,key) -- into this function burnoutTheTires(key) and likewise for unpressedTheKeys, function unpressedTheKeys(player) -- into this function unpressedTheKeys() You could have discovered this yourself with a very simply debug methodology: debug outputs. See outputDebugString and iprint, (and less frequently used for this purpose but still usable: outputChatBox, outputConsole). And get rid of the burnoutTimer table, it is unnecessary, since it only ever stores one value, always under the key equivalent to the localPlayer element. Simply declare a local value (tip: local values, aka upvalues are faster in write and read speed over globals which are actually entries in the _G table and table lookups aren't as fast as upvalues) local burnoutTimer --- ... elsewhere in the code burnoutTimer = setTimer(...) -- ... in another place if isTimer(burnoutTimer) then killTimer(burnoutTimer) end -- etc. You may choose to search-and-replace all occurrences of "player" with "localPlayer" since that's pre-defined, but it's fine to leave as since it's just a local value pointing to the localPlayer element. The following part seems to be remnant of a time when this script was supposed to be serverside? for i,v in ipairs(getElementsByType("player")) do --bindKey("accelerate","down",burnoutTheTires) --bindKey("brake_reverse","down",burnoutTheTires) bindKey("accelerate","up",unpressedTheKeys) bindKey("brake_reverse","up",unpressedTheKeys) end Anyway, completely unnecessary -- onClientResourceStart is triggered for a running client (at the time of resource start on the server) as well as a joining client (aka resource start on the client at time of join), so this results in duplicated bind calls.
  19. Do you possess the server-side for the given script? Or are you attempting to use someone else's client-side script files? This event name seems suspicious, like the author intended it to not work without the server-side files, even if they might not be required.
  20. I'd point out @Shady1's solution does not scale well as the whitelist gets bigger. It would be more ideal to, on start-up, and then periodically (or on command like refreshwhitelist) download the list, store it in a Lua table in a [serial] = bool structure and look-up that table onPlayerConnect, and cancelEvent(true) if serial not present (which also benefits the server process as a good part of player join sequence is aborted earlier, including the construction of a player element). This way you don't download the whole list every time a player joins, parse it every time a player joins, and iterate thought it every time a player joins. Lua table look-ups benefit from hashing, leading to much faster lookup speeds than iterating thought the list, provided the list is large enough (could be as little as 5 entries for benefits to show up but I haven't tested it) (otherwise hashing could be more expensive).
  21. if capacete1 then This checks whether a value is assigned to capacete1 (this value could be anything -- a string, number, userdata, coroutine, element, etc.), not whether it is a valid value for the purposes of your script. If the warning message you get says "expected element", it means you've passed it something that isn't an element (for instance, it could be a userdata reference to an element that's been destroyed by another script). A solution that gets rid of the warning is if isElement(capacete1) then
  22. Pretty sure the most important part is slots[i] = {screenW * x, screenH * (y - scroll), screenW * 0.0483, screenH * 0.0872} replacing table.insert(slots, {screenW * x, screenH * (y - scroll), screenW * 0.0483, screenH * 0.0872}) The table.insert version was continuously adding new entries into the table rather than updating existing values as you scroll.
  23. First of all, admin/staff_manager is not part of the default set of resources for MTA, so next time, tell us exactly what sort of resource you're using, where you got it from, a link to the documentation/user guide if you read it yourself beforehand) Also, please tell us what you've already tried doing to resolve your issue. So far, the error message suggests there's an issue with the resource 'integration' -- is it started? Does it have any errors when it starts? Have you tried restarting it?
  24. Addlibs

    API

    Edit: I've misread the question; the following is an answer to how to create an API that lists the players on your server. There are a number of ways to do this: Query the ASE port (123 above the server port, e.g. 22126 for a server running on 22003) for the information (I'm not sure how the ASE protocol works, you'd have to research it), or Query the server via its HTTP port (here you would need to create a HTTP web access resource which responds with the list of players in an encoding the API client expects, e.g. JSON) (can trigger anti-flood/anti-spam countermeasures on MTA server side if IP is not whitelisted in mtaserver.conf, as it would try to request a fresh list every time anyone loads a page; ideas for cached storage of fetched data below) this requires allowing guest HTTP access to the server (can expose admin functionality to everyone if not careful, so limit admin/webadmin resource access to authenticated users only), or setting up a dedicated user and logging in with it (password is sent via HTTP authenticate headers in plaintext, unencrypted; can expose admin functionality to eavesdropper on network between API client and MTA server; so limit this users ACL/RPC to read only access) or Have the server regularly send updates to a web server and the web server keep a copy of the latest list of players, or Have the server track players on it and update a MySQL database that the webserver connects to. The answer to your question is generally no, unless you plan to run surveillance on the MTA global server masterlist, query every server's player list and create a comprehensive database of who plays on what servers, and then query such a database.
  25. It's a syntax issue -- you're not supposed to use a colon to separate the key from the value in a table. The correct character is an equals sign: local messages = { message1 = " Some message", message2 = " Some message2", } I believe "<name> expected" is referencing OOP style calls like SomeObject:someMethod(), (the only use of the colon in Lua that I can recall of the top of my head), as if you wrote :someMethod() without any object name in front, but Lua expects a object name there.
×
×
  • Create New...