• Content Count

  • Joined

  • Last visited

Everything posted by MrTasty

  1. This is malformed XML (line 9). You forgot a closing angled bracket > <config src='skins.xml' type=''></config>
  2. 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.
  3. I suppose the way would be to interpolate positions between path anchors and set the train's position and rotation to appropriate values every frame. I'm not sure but you may need to set it to be in a derailed state to do that.
  4. 20 or 30 would mean 2 and 3 Hz update rate respectively. These don't sound bad. I'd assume they don't need fixing. What needs fixing are resources that calling between 300 and 600 times a second per client, as that means they're syncing element data every frame, which is very very rarely necessary, and even when so, sync itself is often unnecessary. If you know your element data is consistent between client and server (that is, no client changes the data locally only or the server on server only, for example) than you should be able to use a simple guard if getElementData(elem, "key") ~= intended_value then setElementData(elem, "key", intended_value) end If they are out of sync, they probably don't need syncing or your scripts are badly designed. If they don't need sync you should use setElementData(elem, "key", intended_value, false) However, if you're getting, say, 35 clients each sending 6 Hz updates, the server is receiving 210 updates a second on the server. This number need to be flexible. It must be able to accommodate your server player capacity (player slots), otherwise during peak hours when there's more people, there's more lag.
  5. This is definitely (part of) the problem: This is definitely your fuel system, since we're looking at 3.7k data changes per 10 seconds (6 updates per frame if you run 60 fps, more if lower fps). I can't narrow down exactly which resource it is (but you should probably know) nor which lines specifically, but you could share parts of the fuel system code which call setElementData for key "fuel" so we can help you reduce this number.
  6. These seem to be what's causing you most of the lag. However, these changes don't seem to be associated with any resource. I'd guess that means they're triggered remotely from a client. My bet this is some sort of fuel system, and it probably has every client send fuel updates instead of only the driver and/or element syncer. If you're running the server on 1.5.5 r11856 or above, the debug hook should have more parameters to help narrow down where it's coming from: resource eventResource, string eventName, element eventSource, element eventClient, string eventFilename, int eventLineNumber, resource functionResource, string functionFilename, int functionLineNumber, ...eventArgs if you use preEventFunction instead of preEvent debug hook. Pretty sure functionResource would be the resource of the function that handles the event, which should be more reliable (perhaps?) than eventResource. So you could incorporate inspect(functionResource) -- or getResourceName(functionResource) into one of the debug hook codes above, such as local eventList = {} setTimer( function () if next(eventList) then outputDebugString(inspect(eventList)) eventList = {} end end, 10000, 0 ) addDebugHook( "preEventFunction", function(eventResource, eventName, eventSource, eventClient, eventFilename, eventLineNumber, functionResource, functionFilename, functionLineNumber, ...) local args = { ... } local i = "resource: " .. (eventResource and getResourceName(eventResource) or "[unkown]") .. ", handled by: " .. (functionResource and getResourceName(functionResource) or "[unkown]") .. ", key: " .. tostring(args[1]) .. ", file: " .. tostring(functionFilename) .. ", line-number: " .. tostring(functionLineNumber) eventList[i] = (eventList[i] and eventList[i] + 1) or 1 end, {"onElementDataChange"} )
  7. You don't get errors because connection failed ≠ error 404. If you're getting a 404 error (or the 'There was a problem with the request. Ensure that the resource exists and that the name is spelled correctly.' exception), it means you're connecting to a proper HTTP server (and likely the one you want to connect to). Are you sure there is a resource called 'classes' running on the server, and that it has a function called '_getPlayerCount' and that it is indeed exported for HTTP use in meta.xml?
  8. local eventList = {} setTimer( function () if next(eventList) then outputDebugString(inspect(eventList)) eventList = {} end end, 10000, 0 ) addDebugHook( "preEvent", function ( sourceResource, eventName, eventSource, eventClient, luaFilename, luaLineNumber, ... ) local i = inspect(sourceResource).."/"..eventName.."/"..inspect(eventSource) eventList[i] = (eventList[i] and eventList[i] + 1) or 1 end, "onElementDataChange" ) You could use this slightly adapted IIYAMA's code (only hooking onto onElementDataChange this time) which also includes the name of the resource and a readable identifier for the source element into the list, so you can tell which resource causes the large amount of data changes.
  9. This exception is thrown when the MTA's server HTTP agent returns a 404 error. This means either the resource 'classes' or the function '_getPlayerCount' doesn't exist on the server. This function is called from line 5 of footer.php which is included on line 12 of ucp.php.
  10. Login on the server with an admin account, and type in '/debugscript 3' and tell us what shows up there when you start/restart the resource. If the resource failed to load, you should get a reason for it in F8 console too, if you try to start it.
  11. Individual clients don't have a peer-to-peer connection between each other, so you have to run everything via the server. When a client wants to send a message, relay it through the server via triggerServerEvent and then triggerClientEvent to all clients (with a single call). Anyway, if you're going to do something like that, as a kind of optimization, you may as well just have the /say handler on the serverside instead of the client if all the client does is send it to the server. If you want to play sounds/info2.mp3 when localPlayer uses /say you could just play the sound via chat->NewMessage if source == localPlayer
  12. I'd suggest to not collect debug log for every client, as that will very quickly fill up with irrelevant logs as well as duplicates per every client if an error comes up for all players for example. It would be best to either limit it to collecting debug log from a very small sample of people, or keep on every client a buffer of the last x lines of debug, and send it over if they make a bug/glitch report or something.
  13. MrTasty

    db. update

    triggerServerEvent( "savem9cko", getLocalPlayer(), m9cko) -- line 8 Sends the value of the variable m9cko (which is the text of the editbox) as the 1st parameter of the event savem9cko.
  14. MrTasty

    db. update

    On the client side: guiGetText triggerServerEvent On the server side: addEvent addEventHandler dbExec
  15. MrTasty

    Check BD

    Depending on whether you're using an external MySQL, local SQLite database or built-in SQLite registry.db. For the first two, you need to create a connection via dbConnect. The last one only requires the use of executeSQLQuery. You'll need to do all of it on the server side, using events to send the nickname from the GUI to the server. The query string is pretty much the same between MySQL and SQLite: SELECT [columns1, column2, ...] FROM [table] WHERE [column1] = ? AND [column2] = ? So if you want to check for a nickname being registered, you'll want to do something like this: -- for external MySQL or local SQLite dbQuery( function(queryHandle) -- callback func local result = dbPoll(queryHandle, 0) if #result >= 1 then -- record with such nickname exists else -- no record for this nickname end end, dbConnection, "SELECT nicknane FROM accounts WHERE nickname = ? LIMIT 1", strPlayerNickname ) -- for internal registry.db local result = executeSQLQuery(dbConnection, "SELECT nickname FROM accounts WHERE nickname = ? LIMIT 1", strPlayerNickname) if #result >= 1 then -- record with such nickname exists else -- no record for this nickname end The '?' are used for parameter binding, which is the easiest and probably the safest way to prevent SQL injection. I only select 'nickname' as we don't need any extra information. "LIMIT 1" prevents the SQL server from looking for more that one record, since that already falsifies the proposition that there is no record with that nickname. Note: I am not sure if the given code works out of the box -- not tested. I haven't scripted on MTA for a while and I am unsure of the exact structure of the SQL query return. You might need to do some debugging with iprints if it doesn't work.
  16. MrTasty


    It's quite hard to understand your problem. Is the problem that you don't have a MySQL server or that you don't know it's address/hostname? If you don't have one, you'll need to set one up. If you have a VPS or some other hosting, this should be pretty easy, especially with things like cPanel, and you can always Google around if you're not sure. If your hosting is MTA server-only, then you'll probably want to either find a rentable MySQL database, or migrate to a VPS or something where you can run more than just an MTA server.
  17. MrTasty

    xml vs json

    In which scope does it appear as nil? Right after loading it or later on somewhere else? I have a feeling this is about scope of variables: you load the data into a local variable which means it's only accessible in that same scope/block/function you're working in. Have you tried adding a debug output for 'loadedData' right after fromJSON'ing it?
  18. 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.
  19. MrTasty

    Local voice chat

    I've just released a 3D proximity based voice chat resource which I had sitting on my disk being unused.;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.
  20. 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.
  21. 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.
  22. 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.
  23. 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.