Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by Addlibs

  1. Unable to enter You cannot enter the vehicles, because they are client-side only vehicles -- as it says on the reference wiki, "Note: Vehicles (and other elements) created client-side are only seen by the client that created them, aren't synced and players cannot enter them. They are essentially for display only." To solve this, you need to spawn the vehicles on the server side, preferably with the parent ID as the model, then send information to all clients (and remember the vehicle's model for newer clients connecting later on) that the client-side model ID needs to be changed, and setElementModel client-side over that vehicle. The easiest way to do this is with element data: setElementData on the server, onClientElementDataChange and onClientElementStreamIn with getElementData on the client. Models not changing You seem to be allocating a new model ID every time you want to spawn a vehicle, rather than reusing an ID which has been assigned a modded DFF and TXD. To solve this, you should be using engineRequestModel in the same function you load the mods, that is, where engineImportTXD and engineReplaceModel are. For example, -- vehicles = { -- ["txd_and_dff_name_with_ext"] = {parentID = 429, dynamicID = nil --[[ this entry gets filled at runtime ]]} -- } function allocateAndSkinVehicles() -- renamed function to better reflect its new purpose local txd, dff; for id,vehicle in pairs(vehicles) do if fileExists("vehicles/" .. id .. ".txd") and fileExists("vehicles/" .. id .. ".dff") then local modelid = engineRequestModel("vehicle", vehicle.parentID) -- request a new ID for this vehicle txd = engineLoadTXD("vehicles/" .. id .. ".txd") engineImportTXD(txd, id) dff = engineLoadDFF("vehicles/" .. id .. ".dff") engineImportDFF(dff, id) outputChatBox("Model ID "..id.." changed correctly.") vehicle.dynamicID = id -- save the allocated ID to the vehicle's entry else outputChatBox("Model ID "..id.." couldn't change. REASON: vehicles/".. id ..".txd or vehicles/".. id ..".dff does not exist.") end end end addEventHandler("onClientResourceStart", resourceRoot, allocateAndSkinVehicles, false) -- changed triggering to happen on resource start This way, when the server tells you to change a vehicle's client-side model, either by element data or though events, you need to look up vehicles[customID].dynamicID to get the actual allocated model ID with the particular modification on the vehicle. Note this implementation allows you to use strings for customID, doesn't have to be a number; all it requires is there are .txd and .dff files with that exact name in the ./vehicles directory.
  2. Reference site also mentions that this function is client-only, with the big red "Client-side function" text on the top-right of the page. This means you cannot use this function in a server-side script. "global.lua" is, I assume, both client and server: the server's debug log is telling you that this function is not defined (i.e. is a nil value).
  3. engineRequestModel of type "vehicle" with parent ID 481. This function returns to you an newly allocated ID which you can use with engineReplaceModel and engineImportTXD. However, these IDs do not work server side at all, and are not synchronized across clients either. Client 1's ID for mod 1 may be 100, while Client 2 refers to that same mod as 106. You will need to track these IDs by linking them with something like a model name, perhaps the .dff file's name/path. Server spawns the parent model, sets element data like "model" to "my/mods/mod1.dff". Clients listen for onClientElementStreamIn and onClientElementDataChange to detect this spawn, check if the model's already allocated and assigned, if so, setElementModel clientside to the custom ID for this particular client, else, allocate model, load dffs and txds, setElementModel clientside to newly allocated ID and save the newly allocated ID with the model's dff name/path so subsequently it can be discovered without loading again. Or you could load all anticipated models early, onClientResourceStart. If you strictly control resource loading order and each resource's custom replaces, you can reasonably predict which mod will have which ID, but again, this isn't guaranteed in any way -- maybe it works now but in the future it won't, so don't assume the loading order.
  4. You need to spawnPlayer and fadeCamera on onPlayerJoin.
  5. Don't use getPlayerName (this takes a player element as input and returns a string) but rather getPlayerFromName (this takes a string as input and returns a player element). Note that you will need to add extra code to handle the player connecting and disconnecting, as the player element will change when a client reconnects.
  6. One of the parameters of the dgs mouse click event: In other words, the equivalent of onClientGUIClick is onDgsMouseClickUp, not onDgsMouseClick. The latter is indeed triggered for every mouse button activity (for the press and for the release) while the cursor is over the element. CEGUI click is registered when you release the mouse button while over the element, giving the user an option to cancel a misclick by holding the mouse button, dragging the cursor off the element and releasing.
  7. In that case, you should double check you're not calling showConfirmDelete twice; you can either inspect / show your code for that, or you could add an outputDebugString call at the beginning of the function to see how many times its output. You could use outputDebugString(debug.traceback()) to get information on where exactly the function was called from. If it's being called from a function that handles an event, perhaps the event is triggered multiple times, once per player, perhaps.
  8. The reason one of the copies don't work is because you check if the click event's source is the element in the table buttons and override the values in that table when creating a duplicate copy. If you want both windows to work properly, you should avoid the if source == [some element] then and instead add a false argument after the handler function in addEventHandler. For example addEventHandler("onDgsMouseClick", buttons[11], function() triggerServerEvent("vehlib:deleteVehicle", localPlayer, id) closeConfirmDelete() playSuccess() end, false) That false means the function should not be called for any element other than the element the event is bound to, button[11] at the time the handler was attached; the binding does not change if you later change what buttons[11] references.
  9. When incrementing the attempts counter, check if it just surpassed 10 (that is, it was below 10 before the increment and is now equal or greater than 10), and if so, either start a timer for 24 hours to clear the lock, or store the current timestamp + 86400 sec, and ensure the next time the user attempts to do the same thing, the current timestamp is greater or equal to the previously stored timestamp (of course all of this, per IP). Something like this: local attempts = {} local lockouts = {} function isIPLockedOut(ip) -- check if the lockout needs to be cleared if (lockouts[ip]) then -- if ip was locked out before if (lockouts[ip] < getRealTime().timestamp) then -- if lockout timestamp hasn't been reached return true else -- if lockout timestamp is in the past, lockouts[ip] = nil -- clear the lockout and continue the function attempts[ip] = 0 -- clear the attempts count end end local offenseCount = attempts[ip] or 0 if (offenseCount >= 10) then return true -- if count is above 10, return true end return false -- otherwise return false end function incrementOffenseCount(ip) local offenseCount = attempts[ip] or 0 -- default to 0 offenses if not set if (offenseCount >= limit) then -- if limit exceeded lockouts[ip] = getRealTime().timestamp + 86400 -- lockout for 24h else -- otherwise attempts[ip] = offenseCount + 1 -- increment offense count end end -- example usage function attemptLogin(player, username, password) local ip = getPlayerIP(player) if isIPLockedOut(ip) then local seconds = lockouts[ip] - getRealTime().timestamp local hours = math.floor((seconds%86400)/3600) local mins = math.floor((seconds%3600)/60) outputChatBox(string.format("You've attempted this action too many times. Your IP is blocked for %dh %02dm.", hours, mins), player, 255, 0, 0) else if getAccount(username, password) then logIn(player, username, password) else incrementOffenseCount(ip) -- only call this within a conditional block on isIPLockedOut(ip) == false outputChatBox("Wrong username or password", player, 255, 0, 0) end end end For the above provided code, make sure you only ever call incrementOffenseCount if isIPLockedOut returned false, otherwise you'll be resetting the lockout timer.
  10. You could use getRealTime to get the current timestamp, calculate the seconds remaining until the next scheduled time point (which you can calculate with getTimestamp), and setTimer for the many milliseconds.
  11. It's also important to note the server script usually starts before the client script is ready (especially when the client needs to download the scripts and files), meaning an client-bound event dispatched on onResourceStart can turn up ignored for not being added client-side (as the client script isn't running yet). Best way to correct that is have the client send an event to the server when it is ready (i.e. when the resource starts client-side, after all necessary downloads -- onClientResourceStart), and the server's event handler for that event to triggerClientEvent back.
  12. You cannot directly send an SMS using MTA. However, you can use fetchRemote to communicate with external web APIs such as SMS texting services. These services are typically not free, however. You can send emails with one-time passcodes for free if you already have an HTTP server, using PHP's mail() function for example, or a full SMTP server if you have one. These would be triggered by making a request to a particular webpage/server (that is, fetching/requesting the remote page), which can be done though the aforementioned fetchRemote function.
  13. Every onClientGUIClick event handler's attachment needs propagate set to false: addEventHandler("onClientGUIClick", theButton, function() -- ... end, false -- add a false after the function's end (4th argument of addEventHandler) ) Otherwise the event is attached to the given element's/button's element tree, meaning the event being triggered on any child element or parent element will trigger the handler, which is not what you want with GUIs. The other workaround is, in each function, adding an if-block with condition checking the event's source (this isn't the most efficient though, since the function will still be called unnecessarily, just that it won't do anything. addEventHandler("onClientGUIClick", theButton, function() if source == theButton then -- verify the event's source is the button you attached this function to -- do whatever the function does end end)
  14. if vehType == "Automobile" or "Monster Truck" or "Quad" or "Bike" then evaluates as true because "Monster Truck" evaluates true because meaning it isn't false or nil, "Quad" and "Bike" are likewise evaluated as such. What you probably intended was if vehType == "Automobile" or vehType == "Monster Truck" or vehType == "Quad" or vehType == "Bike" then
  15. Can you also check your mta/logs/clientscript.log file for warnings or errors in your script at the time of crash? Changing getRootElement() to root is mostly a stylistic change, doesn't really impact the functionality of the code at all, except saving you a couple calls. Regarding this code, this code contains a potential vulnerability: Seems like source and player are referring to the same thing? Perhaps it'd be best to assert (i.e. assert(source == player, "Source and player mismatch") -- this will print out the second argument in the debug box if the first argument's evaluation is false) that they are equal, or only use one of these variables. It seems like whoever triggers this event can cause a player's to be teleported but not receive the createCharacter event, perhaps by mistake in your code or by a hacked client sending off maliciously crafted events with a source mismatching the player argument. Regarding your initial issue: Is the createCharacter event triggered anywhere else, and how is the event responsible for callCharacterCreation triggered? A minor correction to my previous statement: I actually tested this and it turns out I was slightly wrong - addEventHandler does not let you register the same function with the same event multiple times, but if its attempted, it prints an error in the debug box (and log), so your render function probably not being called multiple times per frame as I've suggested previously.
  16. How is createCharacter triggered? I think I might see the problem. Do you have a lot of elements spawned (vehicles, peds, objects, etc. even if they're out of the streaming range)? It seems your handler function for createCharacter is attached to the root element and all its children. If you trigger it on the root element, rather than, for example, the target's player element, then what actually happens is the same event is dispatched on each element with the given element as its source, and your handler is attached to be called on any child of root, in other words, your handler gets executed as many times as there are elements. In other words, if you have n elements in the element tree, your handler attaches an new event handler for onClientRender n times! Your createCharacterPanel function gets called n times per frame, or 60n times for a client running 60 fps. A quick fix would be to one of the following: change the triggerClientEvent calls everywhere to use the target player as source (since players rarely have any child elements, the event gets dispatched only once per trigger) (likely the best option here), OR add the fourth argument to addEventHandler for createCharacter set to false, to make the handler only get called for the root element and not its children (less preferable; however you should add that to the onClientRender handler attachment regardless), and/or add an if check in the handler to verify that source == getRootElement() (least preferable option as it means your function still gets called tens or hundreds of times only to exit early when you could avoid calling it at all in those circumstances) Doing options 1 and option 2 and/or 3 at the same time will break functionality, as you'll call with a player as source but the function will expect root and only root, disallowing its children (and the player is a child element of root)
  17. By crash do you mean the MTA client crashes, or are you saying your script "crashes" (i.e. stops working?) If it's an MTA client crash, perhaps you're doing something you're not supposed to be doing in the onClientRender handler function. Apart of dx calls, what other functions do you call within createCharacterPanel? Any create* functions in there? All create* functions outlast a frame and its possible you're spawning so many of them you cause a crash due to loading/streaming, or run out of memory. Perhaps its the setCameraMatrix? Does your client ever crash when suddenly streaming in a different part of the map? Does it happen on different computers or only for you?
  18. The error is telling you that mysql_ping function is not defined. Either this script wasn't written for MTA, or it was written to be used with a server module (modules allow server-side Lua to call functions implemented in C or C++). This code in particular appears to want to handle a MySQL connection; this is very deprecated way of doing things as MTA has built-in support for SQLite and MySQL connections though the dbConnect and other db* functions. To fix your issue, you either need to find the module for which this code was written (not recommended, MySQL modules are old and outdated nowadays), or redesign the code to use MTA's built-in db* functions.
  19. Have you tried attachElements?
  20. Actually, code_descriptions is neither a string nor a number, it is an array type. Meaning if you want to print its string, you need to add [1] to the variable that holds the array, to get the first (and only) value in that array, or use a for each loop to iterate over all strings in that array if there can conceivably be more than one in the actual data. tonumber on a string that does not contain a correctly formed number returns nil, which is the case in your original code; also I'm pretty sure fromJSON takes care of returning numbers correctly from a JSON where they're written as number literals rather than strings, so you shouldn't need to use tonumber unless your input data sometimes returns the number within a string.
  21. No need to disable the HS system if you can modify it, simply add checks like the ones above within the system.
  22. You can either use getTickCount() which counts milliseconds since the client/server started, or you can use setTimer to manually increment a variable until it reaches your desired value. getTickCount requires a tiny bit of basic math, but ultimately more efficient than a creating timer.
  23. You can augment the original weapon system if you replace original weapons with empty meshes and attach custom objects to player hands, however, this won't give you new IDs on its own and you'll need to code in a system to refer to and switch between these weapons yourself.
  24. Have you checked using toggleBrowserDevTools DOM inspector whether the JS executes correctly and updates the DOM, that is, whether the element #orghomelogo's src parameter is indeed what you expect it to be?
  25. You need to base64Encode the output of dxGetTexturePixels(texture) not just texture itself, and you need to prepend "data:image/png;base64," to the encoded base64 if you want it to be interpreted as raw data rather than a URL.
  • Create New...