Jump to content

IIYAMA

Moderators
  • Posts

    6,056
  • Joined

  • Last visited

  • Days Won

    208

Posts posted by IIYAMA

  1. 5 hours ago, Turbe$Z said:

    what wrong with the loop?

     

    You have to index the variable veh one time less. Since the pairs loop did the first index already for you.

    Quote

    dxDrawText(veh[k][1].." | "..veh[k][2], sx/2-370,sy/2-297,sx/2,sy/2, tocolor(255, 255, 255, 220), 3, "default-bold", "left", "top", false, false, false, true )

    dxDrawText(veh[1].." | "..veh[2], sx/2-370,sy/2-297,sx/2,sy/2, tocolor(255, 255, 255, 220), 3, "default-bold", "left", "top", false, false, false, true )
    	
    • Thanks 1
  2. 6 hours ago, Snow-Man said:

    what confuses me that when i get table into another tables inside and when i try to get a return from what?

     

    Sometimes it really helps when you use the iprint function to print the current table. It shows you the current depth and helps you to decide how to index the next table.

     

    When I for example I am working with a secret table.

    iprint(result)

    >

    {
    	["test"] = {
    		{
    			{
    				[133]="found me!"
    			}
    		}
    	}
    }

     

    The first step is to peel it down.

     

    Layer 1

    iprint(result["test"])

    >

    {
    	{
    		{
    			[133]="found me!"
    		}
    	}
    }

     

    Layer 2

    iprint(result["test"][1])

    >

    {
    	{
    		[133]="found me!"
    	}
    }

     

    Layer 3

    iprint(result["test"][1][1])

    >

    {
    	[133]="found me!"
    }

     

    Layer 4

    iprint(result["test"][1][1][133])

    >

    "found me!"

     

     

     

  3. 21 hours ago, Snow-Man said:

    im lost through a lot of tables 

    It helps if you write every table modification inside of a new function. This way you keep your code more readable.

    The following examples are re-written code. You could use this as a start, but make sure to test it, because I didn't test it for you.

     

    local farm = createFarm(123, 1, 1, 1)

     

    local horse = createHorse(player, horseId)
    
    local farm = getFarmFromID (123)
    -- If the farm does exist
    if farm then
        addHorseToFarm(horse, farm)
    end

     

     

    local horseCollection = {} -- all horses
    local farmCollection = {} -- all farms
    
    ---@param player userdata
    ---@param horseId integer
    ---@return table
    function createHorse(player, horseId)
    
        local horse = { id = horseId }
        horseCollection[horseId] = horse
    
        return horse
    end
    
    ---@param farmId integer
    ---@param x number
    ---@param y number
    ---@param z number
    ---@return table
    function createFarm (farmId, x, y, z)
    
        local marker = createMarker(x, y, z)
    
        local farm =  {
            marker = marker, 
            id = farmId, 
            location = { x, y, z }, 
            owner = nil, 
            creator = nil, 
            creationTime = getTickCount(), 
            price = 0, 
            horses = {}
        }
    
        farmCollection[farmId] = farm
        return farm
    end
    
    ---@param farmId integer
    ---@return table|false
    function getFarmFromID (farmId)
        return farmCollection[farmId] or false
    end
    
    ---@param farm table
    ---@param player userdata
    ---@return table
    function setFarmCreator (farm, player)
        farm.creator = player
        return farm
    end
    
    ---@param horse table
    ---@param farm table
    function addHorseToFarm (horse, farm)
        table.insert(farm, horse)
    end
    
    ---@param horse table
    ---@param farm table
    ---@return boolean
    function isHorseInFarm(horse, farm)
        for i=1, #farm do
            if farm[i] == horse then return true end
        end
        return false
    end
    
    ---@param farmId integer
    ---@return boolean
    function doesFarmExist(farmId)
        return farmCollection[farmId] and true or false
    end

     

    • Like 2
  4. 45 minutes ago, JamesDragon said:

    Can i also send arguments to the function?

     

    Yes, as long as you do not break the code.

     

     

    <script>
    	function onRemotePlayerWasted (arg) {
    		/* ... */
    	}
    </script>

     

    addEventHandler("onClientPlayerWasted", root, 
    function ()
    	local arg = 123 -- Or string "\"123\"" | "'123'" | '"123"' | [["123"]]
    	--[[ 
    		Or table through JSON: local arg = '`' .. toJSON({}) .. '`'
    		In JS: const result = JSON.parse(arg)
    	]]
    	executeBrowserJavascript(browser, [[onRemotePlayerWasted(]] .. arg .. [[);]])
    end)

     

    *adjusted JSON example, Must be tested through trial and error.

  5. 39 minutes ago, JamesDragon said:

    f i want for exemple execute a js function when a lua event happen

     

    For example you have an html file with:

    <script>
    	function onRemotePlayerWasted () {
    		/* ... */
    	}
    </script>

    And you inject the following JavaScript to call the function.

    addEventHandler("onClientPlayerWasted", root, 
    function ()
    	executeBrowserJavascript(browser, [[onRemotePlayerWasted();]])
    end)

     

    • Like 1
  6. 2 hours ago, JamesDragon said:

    i work on a ui panel using html

     

    You could try something like this.

    
    -- See https://wiki.multitheftauto.com/wiki/ExecuteBrowserJavascript for the rest of browser the code
    
    function reloadList (browser)
        local list = {
            "a",
            "b"
        }
    
        local htmlItems = {}
        for index, value in ipairs(list) do
            table.insert(htmlItems, [[<li>]] .. value .. [[</li>]])    
        end
    
        local js = [[{
            const list = document.createElement("ul")
            list.innerHTML = `]] .. table.concat(htmlItems, "\n") ..  [[`;
            document.body.append(list);
        }]]
    
        executeBrowserJavascript(browser, js)
    end
    
    addEventHandler("onClientBrowserDocumentReady", browser,
    function ()
        reloadList (source)
    end)

    See: https://wiki.multitheftauto.com/wiki/OnClientBrowserNavigate when having different pages

  7. 5 hours ago, Sr.black said:

    If you want can you change the terms of the winner of the war? the team that kills the largest number in the war zone is the winner and the color of the winning team is determined for the four gangs in the war set the winning teams color area

    Having very complex winning conditions also makes it harder for your players to understand. Maybe it is for the best to keep things simple.

    • Thanks 1
  8. 3 hours ago, Sr.black said:

    Unfortunately after my experience of attacking the areas of all other gangs I discovered the same problem :(

    Something I like to do it is logging the current situation.

    • playerCount in the area (of all teams)
    • score
    • winner

    And then go manually through the system with the current situation until you find the issue.

    • Like 1
  9. 12 hours ago, Sr.black said:

    I hope you have understood my problem

    I took a look at it. But I am not sure if I understand all the rules.

     

    One thing that I can suggest to make it more readable:

    local example = {
    	["Grove Street"] = function (score, playerCount)
    		-- Aztecas or Vagos
    		if score > 0 and (playerCount["Aztecas"] > 0 or playerCount["Vagos"] > 0) and playerCount["Aztecas"] ~= playerCount["Vagos"] then
    			if playerCount["Aztecas"] > playerCount["Vagos"] then 
    				return "Aztecas"
    			end
    			return "Vagos"
    		end
    		-- or Ballas
    		if score > 0 and playerCount["Ballas"] > 0 or playerCount["Grove Street"] == 0 then
    			return "Ballas"
    		end
    		-- or Grove Street
    		return "Grove Street"
    	end
    }
    
    -- local winner = example[areaTeam](score, playerCount)

     

    • Like 1
  10. 6 hours ago, pixel77 said:

    The old triggerServerEvent validating still working or not?

    It does.

    The new method is a utility function, which does everything for you. But also for some parts it forces you to be more strict. For example the: protectedKeys table part.

     

     

    6 hours ago, pixel77 said:
                -- The source for this event is always 'resourceRoot'
                if source ~= resourceRoot then
                    reportNaughtyness( eventName, client, "source" )
                    return
                end

    For resourceRoot you can also block this, instead of reporting. Saves you some lines of code.

    addEvent("onRaiseTheRoof", true)
    addEventHandler("onRaiseTheRoof", resourceRoot,
    function(arg1, arg2)
    
    end, false) -- propagate disable, making sure only resourceRoot can be passed as the source

     

    • Thanks 1
  11. On 13/12/2023 at 17:50, FlorinSzasz said:

    and add a 3d line to it to see where they aim.

    Bullets do not start from the weapon Muzzle position.

    The start position is:
    https://wiki.multitheftauto.com/wiki/GetPedTargetStart

    And the end position:
    https://wiki.multitheftauto.com/wiki/GetPedTargetEnd

    Both can be used to create a vector for the line you are referring to.

     

    This is related to the reason why people can shoot around corners without showing more than their elbows. Since the bullets start more to the right of the weapon.

    • Thanks 1
  12. 5 hours ago, kajahun said:

    I have an achievement list store in the scripts.  How can I store the completed achievements for the users in the SQL?

    As roaddog mentioned, toJSON is the easiest way to dynamic store data. But keep an eye on the limitations of JSON. Only store JavaScript structured default {} objects or [] arrays, nothing in between! (else it will be converted to the default {} object variant)

     

    5 hours ago, kajahun said:

    Maybe a third connection table? Because it is a many-to-many connection

    This would be the most space saving solution. Depending on the amount of players you receive every day, it might be a better solution when there are a lot. Just keep in mind that it will be more work.

     

     

    • Like 1
  13. 20 minutes ago, FlorinSzasz said:

    I want to see your money every 20 - 30 seconds and from 1000$ you go to 999999999$ something is wrong.

    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

    1. Create a value-check table / rollback table:
      1. Stores the value that it is being incremented (within 30 seconds)
      2. Stores the future time, when elapse the incrementing value can be ignored and resets to 0
    2. Create a trigger which triggers on update
      1. Do behaviour of value-check table.
      2. If the value jumps too much UP:
        1. Report to a separate table
        2. Freeze account
        3. Rollback manually / automatic (not recommended)

     

     

     

     

    • Thanks 1
  14. 2 hours ago, FlorinSzasz said:

    I wonder what method should i use ?

    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.

    • Like 1
  15. 51 minutes ago, hendawh said:

    From what you wrote I understand that I should create and export the dbResponse() function in each resource that performs a database query at some point?

    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)
     

  16. 19 hours ago, hendawh said:

    You mentioned that I should prepare two functions (including one in a separate resource) and I don't understand what you meant.

    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" />
    Spoiler
    -- 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

     


     

    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.

     

     

  17. 49 minutes ago, hendawh said:

    Any help?

     

    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

     

     

     

     

     

    • Thanks 1
  18. 9 hours ago, Ryuto said:

    I also wanted to ask if it is possible to change the default fire color, without affecting the original fire.

    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.

  19. 5 hours ago, MGO said:

    if anyone knows any solution plz help.

    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)

     

  20. 17 hours ago, Ryuto said:

    I'm sorry to reopen this post again but I have a question.

    Is there a way to detect if the player is being selected from another external script so I can execute a function?

    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)

     

  21. 32 minutes ago, Ryuto said:

    What functions could I use?

    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.

     

     

×
×
  • Create New...