-
Posts
6,088 -
Joined
-
Last visited
-
Days Won
215
Everything posted by IIYAMA
-
If you are worrying about performance, in that case you might consider using MySQL triggers. Those are as close as it gets to your db, making sure you do not cause overhead and align the value-checks with the update moments. https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html Create a value-check table / rollback table: Stores the value that it is being incremented (within 30 seconds) Stores the future time, when elapse the incrementing value can be ignored and resets to 0 Create a trigger which triggers on update Do behaviour of value-check table. If the value jumps too much UP: Report to a separate table Freeze account Rollback manually / automatic (not recommended)
-
Preventing is better than checking for unusual values. Especially when you can't don't keep track of how they have been changed. This could have been done by an exploit instead of real cheats. I personally consider exploiting almost cheating, since the developers should be responsible to deliver decent code. Back to the subject: Value changes in databases are often done by user input. This should be your primary objective. For example if you have a marker that opens a GUI to transfer money: addEvent("onPlayerTransferMoney", true) addEventHandler("onPlayerTransferMoney", resourceRoot, function (marker, transferAccountId) -- Possible cheating? = hard returns if not isElement(marker) then return end local playerX, playerY, playerZ = getElementPosition(client) local markerX, markerY, markerZ = getElementPosition(marker) if getDistanceBetweenPoints3D ( playerX, playerY, playerZ, markerX, markerY, markerZ ) > 10 then return end -- More user friendly returns if not checkPassiveTimer("event:onPlayerTransferMoney", client, 1000) then return outputChatBox(client, "You can only transfer money every 1 sec.") end -- Source code: https://wiki.multitheftauto.com/wiki/CheckPassiveTimer if not transferAccountId then return end -- resume operations -- end) Check if marker exist Check if player is close enough to the marker Limit execution speed (else users can create database lag by spamming the UI button) Building an anti cheat system to check for anomalies in your database should be a secondary objective. In this case you could start with defining limits. For example a money limit. A soft limit to detect possible cheating and a hard limit to define the impossible. For a soft limit you only report to yourself, as it might be very active player. And for a hard limit, you could for example freeze the money. In this case you could inform the user that you are investigating the issue. > As you might not know if the user itself is responsible for the anomaly or if another user is responsible. That user could also be helpful in finding/knowing the exploit that is responsible for the anomaly.
-
Yes, in every resource that is receiving data from the database manager, you should add an export function with the name dbResponse. (it is important that the name 'dbResponse' is used consistent)
-
A - Resource requesting data B - Database manager A > B The first export function is db_query, located in resource B, which you have already created. -- Resource A makes this call to resource B exports.database_connection:db_query(id, "SELECT * FROM `players`") <!-- Resource B --> <export function="db_query" type="server" /> B > A The second one is dbResponse, located in resource A, which is where the response is returned to, with: -- Resource B makes this call to resource A call( theSourceResource, "dbResponse", id, result, num_affected_rows, last_insert_id ) <!-- Resource A --> <export function="dbResponse" type="server" /> -- Resource A function dbResponse (id, ...) if not callbacks[id] then return end callbacks[id](...) end Not sure if you want to keep your function naming style, else it will be db_response ofcourse.
-
The following examples are part of a project I have build before. You will have to set-up: 2 export function (one each resource) For sending (as you have now) And for receiving You will have to generate an unique identifier for each query, so that each request can be wired back to where you want the data. -- Resource A Identifier = {} function Identifier:newGenerator() return setmetatable( { index = 0 }, { __index = Identifier } ) end function Identifier:generate() local index = self.index + 1 self.index = index return "id:" .. getTickCount() .. "|" .. index end -- For new ID's local identifierGenerator = Identifier:newGenerator() -- function () local id = identifierGenerator:generate() -- end Before you send a request you make sure that there is a destination: -- Resource A callbacks = {} -- function () local id = identifierGenerator:generate() -- Set-up the callback destination, anonymous func if you want to keep going where you left of. callbacks[id] = function (...) iprint(...) end exports.database_connection:db_query(id, "SELECT * FROM `players`") -- end Add wiring for receiving and calling back your callback function: -- Resource A function dbResponse (id, ...) if not callbacks[id] then return end callbacks[id](...) end Keep the wiring going in the db resource, so that the ID goes ping pong: -- Resource B function db_query(id, ...) -- The source resource that made this call (important to rename the pre-defined variable, else it will be lost) local theSourceResource = sourceResource if (db_settings.db_connection) then dbQuery(function(query_handle) local result, num_affected_rows, last_insert_id = dbPoll(query_handle, 0) call( theSourceResource, "dbResponse", id, result, num_affected_rows, last_insert_id ) end, db_settings.db_connection, ...) end end
-
Not sure about replacing it without affecting the original fire. But if you look closely at the fire animations, you can see that it exist out of 2x > 2D animations. Each of them starts at one of the feet. Which you could draw with: dxDrawMaterialLine3D (that is if you have the frames or a shader) Note: Too much of those calls can be demanding, make sure to optimise the textures. Please consider replying on your other topic.
-
Just move the text by 1 pixel: local borderSize = 1 dxDrawText(v.text, sx - (0.5 * width) - borderSize, yPos, sx - (0.5 * width) - borderSize, yPos - (i * fontHeight), tocolor(0,0,0), 1, font, "left", "top", false, false, false) dxDrawText(v.text, sx - (0.5 * width) + borderSize, yPos, sx - (0.5 * width) + borderSize, yPos - (i * fontHeight), tocolor(0,0,0), 1, font, "left", "top", false, false, false) dxDrawText(v.text, sx - (0.5 * width) - borderSize, yPos, sx - (0.5 * width) + borderSize, yPos - (i * fontHeight), tocolor(0,0,0), 1, font, "left", "top", false, false, false) dxDrawText(v.text, sx - (0.5 * width) + borderSize, yPos, sx - (0.5 * width) - borderSize, yPos - (i * fontHeight), tocolor(0,0,0), 1, font, "left", "top", false, false, false) dxDrawText(v.text, sx - (0.5 * width), yPos, sx - (0.5 * width), yPos - (i * fontHeight), tocolor(unpack(v.color)), 1, font, "left", "top", false, false, false)
-
Yes, you use the event system for that. --[[ Ⓒ FLUSHBICEPS ]] addEventHandler("onClientKey", root, function(button, press) if not press then return end if button == "mouse_wheel_up" then selectedIndex = selectedIndex - 1 if selectedIndex < 1 then selectedIndex = #nearbyPlayers end elseif button == "mouse_wheel_down" then selectedIndex = selectedIndex + 1 if selectedIndex > #nearbyPlayers then selectedIndex = 1 end else return end local selectedPlayer = nearbyPlayers[selectedIndex] if not isElement(selectedPlayer) then return end triggerEvent("onClientSelectPlayer", selectedPlayer, selectedIndex) end ) addEvent('onClientSelectPlayer', false) addEventHandler('onClientSelectPlayer', root, function (selectedIndex) iprint('Player-element:', source, ', index:', selectedIndex) end)
-
It is not possible at the moment with the help of gravity functions, because there is only a gravity function for vertical for non vehicle elements. That being said, you might be able to fake it. With some inspiration from the superman resource. But it will not be easy.
-
-- Run this one time local fuelImage = dxCreateTexture ("fuel.png", "argb", true, "clamp") And: dxDrawImageSection(screenX - ScaleY(289), screenY - ScaleY(19), ScaleY(47), ScaleY(-(123*(fuel/100))), 0, 0, ScaleY(47), ScaleY(-(123*(fuel/100))), fuelImage , 0, 0, 0, tocolor(2,153,0,255), false)
-
This is fine. You could transform it in 1 query. But that basically means that you will have to split them up later with Lua. SELECT name, 'time' AS type, time, NULL AS kills FROM general ORDER BY time DESC LIMIT 20 UNION ALL SELECT name, 'kills' AS type, NULL AS time, kills FROM general ORDER BY kills DESC LIMIT 20
- 1 reply
-
- 1
-
-
To solve this problem, you have to use debug lines(iprint) to get to the problem. I fixed here a logic issue, where the player is not removed from the database when he is not ingame. function unmutePlayer(serial, punisherName, ruleNumber, ruleDescription) local player = getPlayerFromSerial(serial) iprint( "<unmutePlayer> mutedPlayers[serial]:", mutedPlayers[serial], ", player:", player ) if mutedPlayers[serial] then if mutedPlayers[serial].unmuteTimer then killTimer(mutedPlayers[serial].unmuteTimer) end mutedPlayers[serial] = nil iprint("<unmutePlayer> remove from database") executeSQLQuery("DELETE FROM punishments WHERE serial = ?", serial) if player then local playerName = getPlayerName(player) setPlayerMuted(player, false) outputChatBox("#00FF00" .. playerName .. " has been unmuted by #FFFFFF" .. punisherName .. " #006600[#FFFFFFReason: Manual unmute#006600]", getRootElement(), 255, 255, 255, true) end elseif player then local playerName = getPlayerName(player) outputChatBox(playerName .. " is not currently muted.", getRootElement(), 255, 255, 255, true) end end And here I added some debug lines, tweaked the select query and removed the loop. addEventHandler("onPlayerJoin", root, function() local player = source local playerName = getPlayerName(player) local playerSerial = getPlayerSerial(player) local query = executeSQLQuery("SELECT * FROM punishments WHERE serial = ? LIMIT 1", playerSerial) iprint('<onPlayerJoin> query:', query) if query and #query > 0 then local row = query[1] local totalDuration = tonumber(row.duration) local ruleNumber = row.reason local ruleDescription = rulesTable[ruleNumber] iprint('<onPlayerJoin>', "totalDuration:", totalDuration, ", difference:", (getRealTime().timestamp - row.timestamp), ", remaining:", totalDuration - (getRealTime().timestamp - row.timestamp)) local remainingDuration = totalDuration - (getRealTime().timestamp - row.timestamp) if remainingDuration <= 0 then executeSQLQuery("DELETE FROM punishments WHERE serial = ?", playerSerial) else setPlayerMuted(player, true) mutedPlayers[playerSerial] = { duration = remainingDuration, startTime = row.timestamp } mutedPlayers[playerSerial].unmuteTimer = setTimer(unmutePlayer, remainingDuration * 1000, 1, playerSerial, "Console", ruleNumber, ruleDescription) outputChatBox("#FFFFFF".. playerName .. " has been muted by #FFFFFFConsole. Reason: RULE #" .. ruleNumber .. ": " .. ruleDescription .. ". Duration: " .. formatTime(remainingDuration), getRootElement(), 255, 255, 255, true) end end end)
-
Don't use playername, use serial. And don't pass the player to this function: function unmutePlayer(player, punisherName, ruleNumber, ruleDescription) Pass over it's serial function unmutePlayer(serial, punisherName, ruleNumber, ruleDescription) local player = getPlayerFromSerial ( serial ) -- utility function copy from: https://wiki.multitheftauto.com/wiki/GetPlayerFromSerial if player then -- is the player in the server? -- inform the player that he has been unmuted end
-
How does the data looks like? And what type does it have?
-
Keep it simple: local hit = processLineOfSight(pedX, pedY, pedZ + distanceOfStart, lineEndX, lineEndY, pedZ+ distanceOfStart, true, false, false) local colorSet = hit and 1 or 2 dxDrawLine3D(pedX, pedY, pedZ + distanceOfStart, lineEndX, lineEndY, pedZ+ distanceOfStart, tocolor(color[colorSet][1], color[colorSet][2], color[colorSet][3]), 2)
-
Normally this combination should be working. But there is one thing that is incorrect, and that is where the event is triggered. Currently it should trigger for all players, but multiplied by the amount of players. Since there is no player target set to where the event is sending to. triggerClientEvent("raceCevent", thePlayer) My recommendations: Serverside triggerClientEvent( thePlayer, -- send to this player "raceCevent", resourceRoot -- source is resourceRoot ) Clientside addEvent("raceCevent", true) addEventHandler("raceCevent", resourceRoot, -- activate the event based on the source resourceRoot function () end, false) -- disable propagate for security reasons
-
It is intended behaviour, your query is doing something that is not allowed: The correct way to solve this (making sure it will always work) is to insert those default values when you insert a new row inside of the database. INSERT INTO accounts ( name, email, ... etc, usedEmails /* the column */ ) VALUES ( ?, ?, ... etc, '[ [ ] ]' /* the intial value */ ) Note: I do not expect that you will be able to fix this in one go. Make sure to make a backup!
-
Not sure if it is related, but there are missing some .. after the variable user: outputChatBox ( user .. " " .. pass .. " " .. mail, player, 255, 0, 0,) This code is located in Checkplayer.lua or Connect.lua?
-
And what if you do: ^[A-Za-z_0-9]+$|^$ | = OR ^ = START $ = END ^$ = allows empty string since there is nothing between the START and the END.
-
In that case you might want to view those in the debug console and check how they differ. iprint(result[1]["password"], pass)
-
if result[2]["password"] == pass then And if you do: if result[1]["password"] == pass then Since normally you want to get the username and password from the same row.
-
The tonumber function is used to convert a string to a number. A string is basically text "abcdef12345!@$#%" and a number is 1234567890. local thisIsAString = "1234" print(type(thisIsAString)) -- string thisIsAString = tonumber(thisIsAString) -- convert a string to number print(type(thisIsAString)) -- number Why is it trying to convert a string to a number? Probably just to be sure, since the code will not work if strings are using for example for the position x, y, z This function is used to add information icons. Please specify errors are shown when using the following command: /addii And mark them in the function with a comment: local id = SmallestID() -- for example this line
-
In that case it might be handy if we know at which line + file the error is occurring.
-
SELECT * FROM `informationicons` AS infoIcons WHERE infoIcons.id IS NULL OR infoIcons.x IS NULL OR infoIcons.y IS NULL OR infoIcons.z IS NULL OR infoIcons.interior IS NULL OR infoIcons.dimension IS NULL Does this query gives you any results? (run in mysql interface) (note this query does only check for null values) If this query does not give you results. Add a log to the following function, in case of a missing x, y or z, it will log the ID of that database row (and abort the loading process). function loadAllInformationIcons() local ticks = getTickCount( ) local counter = 0 local result = mysql:query("SELECT * FROM `informationicons`") while true do local row = mysql:fetch_assoc(result) if not row then break end local id = tonumber(row["id"]) local createdby = tostring(row["createdby"]) local x = tonumber(row["x"]) local y = tonumber(row["y"]) -- bunlar tuhaf local z = tonumber(row["z"]) local rx = tonumber(row["rx"]) local ry = tonumber(row["ry"]) local rz = tonumber(row["rz"]) local interior = tonumber(row["interior"]) local dimension = tonumber(row["dimension"]) local information = tostring(row["information"]) --------------------------------------------- if not x or not y or not z then iprint("Data is corruption starts at ID", id) break end --------------------------------------------- informationicons[id] = createPickup(x, y, z, 3, 1239, 0)--createObject(1239, x, y, z, rx, ry, rz) setElementInterior(informationicons[id], interior) setElementDimension(informationicons[id], dimension) setElementData(informationicons[id], "informationicon:id", id) setElementData(informationicons[id], "informationicon:createdby", createdby) setElementData(informationicons[id], "informationicon:x", x) setElementData(informationicons[id], "informationicon:y", y) setElementData(informationicons[id], "informationicon:z", z) setElementData(informationicons[id], "informationicon:rx", rx) setElementData(informationicons[id], "informationicon:ry", ry) setElementData(informationicons[id], "informationicon:rz", rz) setElementData(informationicons[id], "informationicon:interior", interior) setElementData(informationicons[id], "informationicon:dimension", dimension) setElementData(informationicons[id], "informationicon:information", information) counter = counter + 1 end After wards you can look the id up like this (replace ID 1 with the one in the debug logs) SELECT * FROM `informationicons` AS infoIcons WHERE infoIcons.id = 1